Compare commits

...

20 Commits

Author SHA1 Message Date
ocornut 74527dde75 Texture-based round corners: fixed unused variables warnings. ago%!(EXTRA string=11 months)
Ben Carter 8245fde6d8 Texture-based round corners: Fxed overlapping edge triangles when drawing filled shapes ago%!(EXTRA string=11 months)
Ben Carter bf831fbbe4 Texture-based round corners: Added sampling offset and bitmask for generated radii ago%!(EXTRA string=11 months)
Ben Carter 22b166116a Texture-based round corners: Use separate textures for square corners and fix a bunch of corner-case issues ago%!(EXTRA string=11 months)
ocornut b96279badd Texture-based round corners: Fix building for stroke width 1,2,4 instead of 1,3,4. ago%!(EXTRA string=11 months)
ocornut 478435540c Texture-based round corners: Fix UV coordinates used by RenderWindowResizeGrip() - render was broken ago%!(EXTRA string=11 months)
ocornut 69fa83fef6 Texture-based round corners: Moved RenderResizeGripWithTex to RenderWindowResizeGrip ago%!(EXTRA string=11 months)
ocornut 4fc35056d3 Texture-based round corners: Tweaks, renaming to match tex lines branch. ago%!(EXTRA string=11 months)
omar f632d84771 Texture-based round corners: Default circle segment count to 0 + fix warnings, remove unused macro ago%!(EXTRA string=11 months)
omar d751a608f9 Texture-based round corners: Removed polling for Shift key in draw functions, moved that responsibility to demo code. ago%!(EXTRA string=11 months)
Ben Carter eac25ce8dd Texture-based round corners: Added support for multiple stroke widths ago%!(EXTRA string=11 months)
Ben Carter 0c0b6cff24 Texture-based round corners: Refactoring and upgrades ago%!(EXTRA string=11 months)
Ben Carter 8dff3ce04c Texture-based round corners: Added support for window grip rendering ago%!(EXTRA string=11 months)
Ben Carter cb9d412ad8 Texture-based round corners: Adding style/drawlist flags for rounded corner textures ago%!(EXTRA string=11 months)
omar ef4397331a Texture-based round corners: Minor tweaks ago%!(EXTRA string=11 months)
Ben Carter 06f1c21bd0 Texture-based round corners: Optimized texture-based rounded rectangle/circle code for better CPU performance at the cost of fill-rate ago%!(EXTRA string=11 months)
omar bf8647a889 Texture-based round corners: Using ImFabs() + Minor coding style fixes for consistency ago%!(EXTRA string=11 months)
Ben Carter 20f1feca02 Texture-based round corners: Fixed not drawing correctly with small radii ago%!(EXTRA string=11 months)
omar acdfb18d21 Texture-based round corners: Make FIXME consistent so they can be grepped. Tidying and added notes of things to fix. ago%!(EXTRA string=11 months)
Francisco Demartino fc9c4ec640 Texture-based round corners: Store rounded corners in texture to use 1 quad per corner. ago%!(EXTRA string=11 months)
  1. 32
      imgui.cpp
  2. 16
      imgui.h
  3. 174
      imgui_demo.cpp
  4. 878
      imgui_draw.cpp
  5. 15
      imgui_internal.h

@ -1263,6 +1263,7 @@ ImGuiStyle::ImGuiStyle()
AntiAliasedLines = true; // Enable anti-aliased lines/borders. Disable if you are really tight on CPU/GPU.
AntiAliasedLinesUseTex = true; // Enable anti-aliased lines/borders using textures where possible. Require backend to render with bilinear filtering (NOT point/nearest filtering).
AntiAliasedFill = true; // Enable anti-aliased filled shapes (rounded rectangles, circles, etc.).
RoundCornersUseTex = true; // Enable using textures instead of strokes to draw rounded corners/circles where possible.
CurveTessellationTol = 1.25f; // Tessellation tolerance when using PathBezierCurveTo() without a specific number of segments. Decrease for highly tessellated curves (higher quality, more polygons), increase to reduce quality.
CircleTessellationMaxError = 0.30f; // Maximum error (in pixels) allowed when using AddCircle()/AddCircleFilled() or drawing rounded corner rectangles with no explicit segment count specified. Decrease for higher quality but more geometry.
@ -4634,6 +4635,8 @@ static void SetupDrawListSharedData()
g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedFill;
if (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset)
g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AllowVtxOffset;
if (g.Style.RoundCornersUseTex && !(g.IO.Fonts->Flags & ImFontAtlasFlags_NoBakedRoundCorners))
g.DrawListSharedData.InitialFlags |= ImDrawListFlags_RoundCornersUseTex;
}
void ImGui::NewFrame()
@ -5910,13 +5913,14 @@ struct ImGuiResizeGripDef
ImVec2 CornerPosN;
ImVec2 InnerDir;
int AngleMin12, AngleMax12;
ImDrawFlags CornerFlags;
};
static const ImGuiResizeGripDef resize_grip_def[4] =
{
{ ImVec2(1, 1), ImVec2(-1, -1), 0, 3 }, // Lower-right
{ ImVec2(0, 1), ImVec2(+1, -1), 3, 6 }, // Lower-left
{ ImVec2(0, 0), ImVec2(+1, +1), 6, 9 }, // Upper-left (Unused)
{ ImVec2(1, 0), ImVec2(-1, +1), 9, 12 } // Upper-right (Unused)
{ ImVec2(1, 1), ImVec2(-1, -1), 0, 3, ImDrawFlags_RoundCornersBottomRight }, // Lower-right
{ ImVec2(0, 1), ImVec2(+1, -1), 3, 6, ImDrawFlags_RoundCornersBottomLeft }, // Lower-left
{ ImVec2(0, 0), ImVec2(+1, +1), 6, 9, ImDrawFlags_RoundCornersTopLeft }, // Upper-left (Unused)
{ ImVec2(1, 0), ImVec2(-1, +1), 9,12, ImDrawFlags_RoundCornersTopRight }, // Upper-right (Unused)
};
// Data for resizing from borders
@ -6304,10 +6308,20 @@ void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar
continue;
const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n];
const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPosN);
window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(window_border_size, resize_grip_draw_size) : ImVec2(resize_grip_draw_size, window_border_size)));
window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(resize_grip_draw_size, window_border_size) : ImVec2(window_border_size, resize_grip_draw_size)));
window->DrawList->PathArcToFast(ImVec2(corner.x + grip.InnerDir.x * (window_rounding + window_border_size), corner.y + grip.InnerDir.y * (window_rounding + window_border_size)), window_rounding, grip.AngleMin12, grip.AngleMax12);
window->DrawList->PathFillConvex(col);
ImVec2 grip_corner = corner;
grip_corner.x += grip.InnerDir.x * window_border_size;
grip_corner.y += grip.InnerDir.y * window_border_size;
// Try and use a rounded texture to draw the grip
if (!RenderWindowResizeGrip(window->DrawList, grip_corner, (unsigned int)window_rounding, (unsigned int)(resize_grip_draw_size - window_border_size), grip.CornerFlags, col))
{
// Fall back to using geometry to draw the whole grip if texture-based draw failed
window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(window_border_size, resize_grip_draw_size) : ImVec2(resize_grip_draw_size, window_border_size)));
window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(resize_grip_draw_size, window_border_size) : ImVec2(window_border_size, resize_grip_draw_size)));
window->DrawList->PathArcToFast(ImVec2(corner.x + grip.InnerDir.x * (window_rounding + window_border_size), corner.y + grip.InnerDir.y * (window_rounding + window_border_size)), window_rounding, grip.AngleMin12, grip.AngleMax12);
window->DrawList->PathFillConvex(col);
}
}
}
@ -7461,6 +7475,8 @@ void ImGui::SetCurrentFont(ImFont* font)
ImFontAtlas* atlas = g.Font->ContainerAtlas;
g.DrawListSharedData.TexUvWhitePixel = atlas->TexUvWhitePixel;
g.DrawListSharedData.TexUvLines = atlas->TexUvLines;
g.DrawListSharedData.TexRoundCornerData = &atlas->TexRoundCornerData;
g.DrawListSharedData.TexSquareCornerData = &atlas->TexSquareCornerData;
g.DrawListSharedData.Font = g.Font;
g.DrawListSharedData.FontSize = g.FontSize;
}

@ -2079,6 +2079,7 @@ struct ImGuiStyle
bool AntiAliasedLines; // Enable anti-aliased lines/borders. Disable if you are really tight on CPU/GPU. Latched at the beginning of the frame (copied to ImDrawList).
bool AntiAliasedLinesUseTex; // Enable anti-aliased lines/borders using textures where possible. Require backend to render with bilinear filtering (NOT point/nearest filtering). Latched at the beginning of the frame (copied to ImDrawList).
bool AntiAliasedFill; // Enable anti-aliased edges around filled shapes (rounded rectangles, circles, etc.). Disable if you are really tight on CPU/GPU. Latched at the beginning of the frame (copied to ImDrawList).
bool RoundCornersUseTex; // Enable using textures instead of strokes to draw rounded corners/circles where possible.
float CurveTessellationTol; // Tessellation tolerance when using PathBezierCurveTo() without a specific number of segments. Decrease for highly tessellated curves (higher quality, more polygons), increase to reduce quality.
float CircleTessellationMaxError; // Maximum error (in pixels) allowed when using AddCircle()/AddCircleFilled() or drawing rounded corner rectangles with no explicit segment count specified. Decrease for higher quality but more geometry.
ImVec4 Colors[ImGuiCol_COUNT];
@ -2752,6 +2753,7 @@ enum ImDrawListFlags_
ImDrawListFlags_AntiAliasedLinesUseTex = 1 << 1, // Enable anti-aliased lines/borders using textures when possible. Require backend to render with bilinear filtering (NOT point/nearest filtering).
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_RoundCornersUseTex = 1 << 4, // Enable using textures instead of strokes to draw rounded corners/circles where possible.
};
// Draw command list
@ -2983,6 +2985,16 @@ struct ImFontGlyphRangesBuilder
IMGUI_API void BuildRanges(ImVector<ImWchar>* out_ranges); // Output new ranges
};
// Data for texture-based rounded corners for a given radius
struct ImFontRoundedCornerData
{
ImVec4 TexUvFilled; // UV of filled round corner quad in the atlas (only valid when stroke width is 1)
ImVec4 TexUvStroked; // UV of stroked round corner quad in the atlas
float ParametricStrokeWidth; // Pre-calculated value for stroke width divided by the radius
int RectId; // Rect ID in the atlas, or -1 if there is no data
bool StrokedUsesAlternateUVs; // True if stroked drawing should use the alternate (i.e. other corner) UVs
};
// See ImFontAtlas::AddCustomRectXXX functions.
struct ImFontAtlasCustomRect
{
@ -3003,6 +3015,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:
@ -3119,6 +3132,9 @@ struct ImFontAtlas
int PackIdMouseCursors; // Custom texture rectangle ID for white pixel and mouse cursors
int PackIdLines; // Custom texture rectangle ID for baked anti-aliased lines
ImVector<ImFontRoundedCornerData> TexRoundCornerData; // Data for texture-based round corners indexed by radius/size (from 1 to ImFontAtlasRoundCornersMaxSize) and stroke width (from 1 to ImFontAtlasRoundCornersMaxStrokeWidth), with index = stroke_width_index + (radius_index * ImFontAtlasRoundCornersMaxStrokeWidth).
ImVector<ImFontRoundedCornerData> TexSquareCornerData; // The same as TexRoundCornerData, but with square corners instead of rounded ones
// [Obsolete]
//typedef ImFontAtlasCustomRect CustomRect; // OBSOLETED in 1.72+
//typedef ImFontGlyphRangesBuilder GlyphRangesBuilder; // OBSOLETED in 1.67+

@ -245,6 +245,177 @@ ImGuiDemoMarkerCallback GImGuiDemoMarkerCallback = NULL;
void* GImGuiDemoMarkerCallbackUserData = NULL;
#define IMGUI_DEMO_MARKER(section) do { if (GImGuiDemoMarkerCallback != NULL) GImGuiDemoMarkerCallback(__FILE__, __LINE__, section, GImGuiDemoMarkerCallbackUserData); } while (0)
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
static void GetVtxIdxDelta(ImDrawList* dl, int* vtx, int *idx)
{
static int vtx_n, idx_n;
static int vtx_o, idx_o;
vtx_n = dl->VtxBuffer.Size;
idx_n = dl->IdxBuffer.Size;
*vtx = vtx_n - vtx_o;
*idx = idx_n - idx_o;
vtx_o = vtx_n;
idx_o = idx_n;
}
// https://github.com/ocornut/imgui/issues/1962
static void TestTextureBasedRender()
{
ImGuiIO& io = ImGui::GetIO();
ImGuiStyle& style = ImGui::GetStyle();
ImGui::Begin("tex_round_corners");
bool old_round_corners_use_tex = style.RoundCornersUseTex;
style.RoundCornersUseTex ^= io.KeyShift;
if (ImGui::Checkbox("style.RoundCornersUseTex (hold SHIFT to toggle)", &style.RoundCornersUseTex))
old_round_corners_use_tex = !old_round_corners_use_tex;
if (style.RoundCornersUseTex)
ImGui::GetWindowDrawList()->Flags |= ImDrawListFlags_RoundCornersUseTex;
else
ImGui::GetWindowDrawList()->Flags &= ~ImDrawListFlags_RoundCornersUseTex;
static float radius = 16.0f; // ImFontAtlasRoundCornersMaxSize * 0.5f;
static int segments = 20;
static int ngon_segments = 6;
ImGui::SliderFloat("radius", &radius, 0.0f, 64.0f /*(float)ImFontAtlasRoundCornersMaxSize*/, "%.0f");
static int width = 180;
static int height = 180;
ImGui::SliderInt("width", &width, 1, 200);
ImGui::SliderInt("height", &height, 1, 200);
static float stroke_width = 1.0f;
ImGui::SliderFloat("stroke_width", &stroke_width, 1.0f, 10.0f, "%.0f");
int vtx_n = 0;
int idx_n = 0;
ImDrawList* draw_list = ImGui::GetWindowDrawList();
{
ImGui::BeginGroup();
ImGui::PushItemWidth(120);
ImGui::SliderInt("segments", &segments, 0, 100);
ImGui::PopItemWidth();
{
ImGui::Button("##1", ImVec2(200, 200));
GetVtxIdxDelta(draw_list, &vtx_n, &idx_n);
ImVec2 min = ImGui::GetItemRectMin();
ImVec2 size = ImGui::GetItemRectSize();
draw_list->AddCircleFilled(ImVec2(min.x + size.x * 0.5f, min.y + size.y * 0.5f), radius, IM_COL32(255,0,255,255), segments);
GetVtxIdxDelta(draw_list, &vtx_n, &idx_n);
ImGui::Text("AddCircleFilled\n %d vtx, %d idx", vtx_n, idx_n);
}
{
ImGui::Button("##2", ImVec2(200, 200));
GetVtxIdxDelta(draw_list, &vtx_n, &idx_n);
ImVec2 min = ImGui::GetItemRectMin();
ImVec2 size = ImGui::GetItemRectSize();
draw_list->AddCircle(ImVec2(min.x + size.x * 0.5f, min.y + size.y * 0.5f), radius, IM_COL32(255,0,255,255), segments, stroke_width);
GetVtxIdxDelta(draw_list, &vtx_n, &idx_n);
ImGui::Text("AddCircle\n %d vtx, %d idx", vtx_n, idx_n);
}
ImGui::EndGroup();
}
ImGui::SameLine();
{
ImGui::BeginGroup();
static ImDrawFlags corner_flags = ImDrawFlags_RoundCornersAll;
ImGui::CheckboxFlags("TL", (unsigned int*)&corner_flags, ImDrawFlags_RoundCornersTopLeft);
ImGui::SameLine();
ImGui::CheckboxFlags("TR", (unsigned int*)&corner_flags, ImDrawFlags_RoundCornersTopRight);
ImGui::SameLine();
ImGui::CheckboxFlags("BL", (unsigned int*)&corner_flags, ImDrawFlags_RoundCornersBottomLeft);
ImGui::SameLine();
ImGui::CheckboxFlags("BR", (unsigned int*)&corner_flags, ImDrawFlags_RoundCornersBottomRight);
{
ImGui::Button("##3", ImVec2(200, 200));
ImVec2 size = ImGui::GetItemRectSize();
ImVec2 r_min = ImVec2(ImGui::GetItemRectMin().x + ((size.x - width) * 0.5f), ImGui::GetItemRectMin().y + ((size.y - height) * 0.5f));
ImVec2 r_max = ImVec2(r_min.x + width, r_min.y + height);
GetVtxIdxDelta(draw_list, &vtx_n, &idx_n);
draw_list->AddRectFilled(r_min, r_max, IM_COL32(255,0,255,255), radius, corner_flags ? corner_flags : ImDrawFlags_RoundCornersNone);
GetVtxIdxDelta(draw_list, &vtx_n, &idx_n);
ImGui::Text("AddRectFilled\n %d vtx, %d idx", vtx_n, idx_n);
}
{
ImGui::Button("##4", ImVec2(200, 200));
ImVec2 size = ImGui::GetItemRectSize();
ImVec2 r_min = ImVec2(ImGui::GetItemRectMin().x + ((size.x - width) * 0.5f), ImGui::GetItemRectMin().y + ((size.y - height) * 0.5f));
ImVec2 r_max = ImVec2(r_min.x + width, r_min.y + height);
GetVtxIdxDelta(draw_list, &vtx_n, &idx_n);
draw_list->AddRect(r_min, r_max, IM_COL32(255,0,255,255), radius, corner_flags, stroke_width);
GetVtxIdxDelta(draw_list, &vtx_n, &idx_n);
ImGui::Text("AddRect\n %d vtx, %d idx", vtx_n, idx_n);
}
ImGui::EndGroup();
}
ImGui::SameLine();
{
ImGui::BeginGroup();
ImGui::PushItemWidth(120);
ImGui::SliderInt("ngon_segments", &ngon_segments, 3, 16);
ImGui::PopItemWidth();
{
ImGui::Button("##3", ImVec2(200, 200));
GetVtxIdxDelta(draw_list, &vtx_n, &idx_n);
ImVec2 min = ImGui::GetItemRectMin();
ImVec2 size = ImGui::GetItemRectSize();
draw_list->AddNgonFilled(ImVec2(min.x + size.x * 0.5f, min.y + size.y * 0.5f), radius, IM_COL32(255, 0, 255, 255), ngon_segments);
GetVtxIdxDelta(draw_list, &vtx_n, &idx_n);
ImGui::Text("AddNgonFilled\n %d vtx, %d idx", vtx_n, idx_n);
}
{
ImGui::Button("##4", ImVec2(200, 200));
GetVtxIdxDelta(draw_list, &vtx_n, &idx_n);
ImVec2 min = ImGui::GetItemRectMin();
ImVec2 size = ImGui::GetItemRectSize();
draw_list->AddNgon(ImVec2(min.x + size.x * 0.5f, min.y + size.y * 0.5f), radius, IM_COL32(255, 0, 255, 255), ngon_segments, stroke_width);
GetVtxIdxDelta(draw_list, &vtx_n, &idx_n);
ImGui::Text("AddNgon\n %d vtx, %d idx", vtx_n, idx_n);
}
ImGui::EndGroup();
}
ImGui::Separator();
ImGui::Text("Style");
ImGui::SliderFloat("FrameBorderSize", &style.FrameBorderSize, 0.0f, 3.0f, "%.0f");
ImGui::SliderFloat("FrameRounding", &style.FrameRounding, 0.0f, 100.0f, "%.0f");
ImGui::SliderFloat("WindowRounding", &style.WindowRounding, 0.0f, 100.0f, "%.0f");
// Show atlas
ImGui::Text("Atlas");
ImFontAtlas* atlas = ImGui::GetIO().Fonts;
ImGui::Image(atlas->TexID, ImVec2((float)atlas->TexWidth, (float)atlas->TexHeight), ImVec2(0, 0), ImVec2(1, 1), ImColor(255, 255, 255, 255), ImColor(255, 255, 255, 128));
style.RoundCornersUseTex = old_round_corners_use_tex;
ImGui::End();
}
//-----------------------------------------------------------------------------
// [SECTION] Demo Window / ShowDemoWindow()
//-----------------------------------------------------------------------------
@ -426,6 +597,8 @@ void ImGui::ShowDemoWindow(bool* p_open)
ImGui::Text("dear imgui says hello! (%s) (%d)", IMGUI_VERSION, IMGUI_VERSION_NUM);
ImGui::Spacing();
TestTextureBasedRender();
IMGUI_DEMO_MARKER("Help");
if (ImGui::CollapsingHeader("Help"))
{
@ -6905,6 +7078,7 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref)
HelpMarker("Faster lines using texture data. Require backend to render with bilinear filtering (not point/nearest filtering).");
ImGui::Checkbox("Anti-aliased fill", &style.AntiAliasedFill);
ImGui::Checkbox("Rounded corner textures", &style.RoundCornersUseTex);
ImGui::PushItemWidth(ImGui::GetFontSize() * 8);
ImGui::DragFloat("Curve Tessellation Tolerance", &style.CurveTessellationTol, 0.02f, 0.10f, 10.0f, "%.2f");
if (style.CurveTessellationTol < 0.10f) style.CurveTessellationTol = 0.10f;

@ -1396,12 +1396,372 @@ void ImDrawList::AddLine(const ImVec2& p1, const ImVec2& p2, ImU32 col, float th
PathStroke(col, 0, thickness);
}
// Add instructions to draw a rectangle with rounded corners to the draw list
// a is the top-left coordinate of the rectangle, b is the bottom-right
// flags should contains a mask indicating which corners should be drawn rounded
// Returns true if the rectangle was drawn, false for some reason it couldn't
// be (in which case the caller should try again with the regular path drawing API)
// We are using the textures generated by ImFontAtlasBuildRenderRoundCornersTexData()
inline bool AddRoundCornerRect(ImDrawList* draw_list, const ImVec2& a, const ImVec2& b, ImU32 col, float rounding, float thickness, ImDrawFlags flags, bool fill)
{
if (!(draw_list->Flags & ImDrawListFlags_RoundCornersUseTex)) // Disabled by the draw list flags
return false;
#if 1
flags = FixRectCornerFlags(flags);
rounding = ImMin(rounding, ImFabs(b.x - a.x) * (((flags & ImDrawFlags_RoundCornersTop) == ImDrawFlags_RoundCornersTop) || ((flags & ImDrawFlags_RoundCornersBottom) == ImDrawFlags_RoundCornersBottom) ? 0.5f : 1.0f) - 1.0f);
rounding = ImMin(rounding, ImFabs(b.y - a.y) * (((flags & ImDrawFlags_RoundCornersLeft) == ImDrawFlags_RoundCornersLeft) || ((flags & ImDrawFlags_RoundCornersRight) == ImDrawFlags_RoundCornersRight) ? 0.5f : 1.0f) - 1.0f);
#endif
const ImDrawListSharedData* data = draw_list->_Data;
IM_ASSERT_PARANOID(!(data->Font->ContainerAtlas->Flags & ImFontAtlasFlags_NoBakedRoundCorners)); // No data in font
// Filled rectangles have no stroke width
const int stroke_width = fill ? 1 : (int)thickness;
if (stroke_width <= 0 || (int)stroke_width > ImFontAtlasRoundCornersMaxStrokeWidth)
return false; // We can't handle this
// If we have a >1 stroke width, we actually need to increase the radius appropriately as well to match how the geometry renderer does things
const int rad = (int)rounding + (stroke_width - 1);
// We don't support zero radius
if (rad <= 0 || rad > ImFontAtlasRoundCornersMaxSize)
return false; // We can't handle this
// This is a very awkward special case - if two opposing corners are curved *and* the width/height of the rectangle is <= 2x radius, the non-curved corner overlaps with the curved one
// Technically this is fixable but it's a major PITA to do so instead we just don't support that (hopefully very rare) case
const ImDrawFlags corner_flags = (flags & ImDrawFlags_RoundCornersMask_);
if ((((b.x - a.x) <= (rad * 2)) || ((b.y - a.y) <= (rad * 2))) &&
((corner_flags == (ImDrawFlags_RoundCornersTopLeft | ImDrawFlags_RoundCornersBottomRight)) || (corner_flags == (ImDrawFlags_RoundCornersTopRight | ImDrawFlags_RoundCornersBottomLeft))))
return false;
const int square_rad = stroke_width + (stroke_width - 1); // Radius to use for square corners and sides - because increasing stroke width grows the line on both sides, we need to do this slightly odd calculation
// Another awkward special case - if rectangle is smaller than the stroke width then we can get bits of one corner poking out from the other at small sizes when we draw a non-filled rect with a mix of rounded and square corners
// (technically this test can be refined to check for possible left/right and top/bottom clashes independently, but it's almost certainly not worth the added complexity)
if ((((b.x - a.x) <= square_rad) || ((b.y - a.y) <= square_rad)) && (!fill) &&
(corner_flags != 0) && (corner_flags != ImDrawFlags_RoundCornersAll))
return false;
const unsigned int index = (stroke_width - 1) + ((rad - 1) * ImFontAtlasRoundCornersMaxStrokeWidth);
ImFontRoundedCornerData& round_corner_data = (*data->TexRoundCornerData)[index];
const unsigned int square_index = (stroke_width - 1) + ((square_rad - 1) * ImFontAtlasRoundCornersMaxStrokeWidth);
ImFontRoundedCornerData& square_corner_data = (*data->TexSquareCornerData)[square_index];
if ((round_corner_data.RectId < 0) || (square_corner_data.RectId < 0))
return false; // No data for this configuration
IM_ASSERT(data->Font->ContainerAtlas->TexID == draw_list->_TextureIdStack.back()); // Use high-level ImGui::PushFont() or low-level ImDrawList::PushTextureId() to change font.
// Calculate UVs for the three points we are interested in from the texture
// - corner_uv[0] is the innermost point of the circle (solid for filled circles)
// - corner_uv[1] is either straight down or across from it (depending on if we are using the filled or stroked version)
// - corner_uv[2] is diagonally across from it
// - corner_uv[1] is always solid (either inside the circle or on the line), whilst corner_uv[2] is always blank
// This represents a 45 degree "wedge" of circle, which then gets mirrored here to produce a 90 degree curve
// See ImFontAtlasBuildRenderRoundCornersTexData() for more details of the texture contents
// If use_alternative_uvs is true then this means we are drawing a stroked texture that has been packed into the "filled"
// corner of the area on the texture page, so we need to calculate UVs appropriately
const ImVec4& round_uvs = fill ? round_corner_data.TexUvFilled : round_corner_data.TexUvStroked;
const bool round_use_alternative_uvs = fill | round_corner_data.StrokedUsesAlternateUVs;
const ImVec2 round_corner_uv[3] =
{
ImVec2(round_uvs.x, round_uvs.y),
round_use_alternative_uvs ? ImVec2(round_uvs.x, round_uvs.w) : ImVec2(round_uvs.z, round_uvs.y),
ImVec2(round_uvs.z, round_uvs.w)
};
// Do the same for square corners
const ImVec4& square_uvs = fill ? square_corner_data.TexUvFilled : square_corner_data.TexUvStroked;
const bool square_use_alternative_uvs = fill | square_corner_data.StrokedUsesAlternateUVs;
const ImVec2 square_corner_uv[3] =
{
ImVec2(square_uvs.x, square_uvs.y),
square_use_alternative_uvs ? ImVec2(square_uvs.x, square_uvs.w) : ImVec2(square_uvs.z, square_uvs.y),
ImVec2(square_uvs.z, square_uvs.w)
};
// In this code A-D represent the four corners of the rectangle, going clockwise from the top-left:
//
// A---B
// | |
// D---C
const bool ba = (flags & ImDrawFlags_RoundCornersTopLeft) != 0;
const bool bb = (flags & ImDrawFlags_RoundCornersTopRight) != 0;
const bool bc = (flags & ImDrawFlags_RoundCornersBottomRight) != 0;
const bool bd = (flags & ImDrawFlags_RoundCornersBottomLeft) != 0;
// Flags indicating which sides should use the rounded texture
const bool side_rounded_l = fill && (ba || bd);
const bool side_rounded_r = fill && (bb || bc);
const bool side_rounded_t = fill && (ba || bb);
const bool side_rounded_b = fill && (bd || bc);
// UVs to use for each corner and the edges
const ImVec2* corner_uv_a = ba ? round_corner_uv : square_corner_uv;
const ImVec2* corner_uv_b = bb ? round_corner_uv : square_corner_uv;
const ImVec2* corner_uv_c = bc ? round_corner_uv : square_corner_uv;
const ImVec2* corner_uv_d = bd ? round_corner_uv : square_corner_uv;
const ImVec2* edge_uv_l = side_rounded_l ? round_corner_uv : square_corner_uv;
const ImVec2* edge_uv_r = side_rounded_r ? round_corner_uv : square_corner_uv;
const ImVec2* edge_uv_t = side_rounded_t ? round_corner_uv : square_corner_uv;
const ImVec2* edge_uv_b = side_rounded_b ? round_corner_uv : square_corner_uv;
// The base vertices for the rectangle
//
// C are the corner vertices, I the interior ones,
// and M the intermediate points on the edge of each
// rounded section, as shown below:
//
// CA--MAY--------MBY--CB
// | |
// MAX IA--------IB MBX
// | | | |
// | | | |
// MDX ID--------IC MCX
// | |
// CD--MDY--------MCY--CC
// Adjust size to account for the fact that wider strokes draw "outside the box"
const float stroke_width_size_expansion = stroke_width - 1.0f;
// The thickness of each edge piece
const int left_side_thickness = side_rounded_l ? rad : square_rad;
const int right_side_thickness = side_rounded_r ? rad : square_rad;
const int top_side_thickness = side_rounded_t ? rad : square_rad;
const int bottom_side_thickness = side_rounded_b ? rad : square_rad;
// The sizes of the corner pieces
const int size_a = ba ? rad : square_rad;
const int size_b = bb ? rad : square_rad;
const int size_c = bc ? rad : square_rad;
const int size_d = bd ? rad : square_rad;
const ImVec2 ca(a.x - stroke_width_size_expansion, a.y - stroke_width_size_expansion), cb(b.x + stroke_width_size_expansion, a.y - stroke_width_size_expansion);
const ImVec2 may(ca.x + size_a, ca.y), mby(cb.x - size_b, cb.y);
const ImVec2 max(ca.x, ca.y + size_a), mbx(cb.x, cb.y + size_b);
const ImVec2 ia(ca.x + size_a, ca.y + size_a), ib(cb.x - size_b, cb.y + size_b);
const ImVec2 cc(b.x + stroke_width_size_expansion, b.y + stroke_width_size_expansion), cd(a.x - stroke_width_size_expansion, b.y + stroke_width_size_expansion);
const ImVec2 mdx(cd.x, cd.y - size_d), mcx(cc.x, cc.y - size_c);
const ImVec2 mdy(cd.x + size_d, cd.y), mcy(cc.x - size_c, cc.y);
const ImVec2 id(cd.x + size_d, cd.y - size_d), ic(cc.x - size_c, cc.y - size_c);
// Positions for edge vertices
//
// Each letter of the name refers to one of (t)op, (b)ottom, (l)eft or (r)ight
// The first letter is the edge, and the second and third are the position on that edge, so for example:
// tbr = (t)op edge, (b)ottom (r)ight vertex
const ImVec2 ttl = may;
const ImVec2 ttr = mby;
const ImVec2 tbr(mby.x, mby.y + (fill ? size_b : top_side_thickness));
const ImVec2 tbl(may.x, may.y + (fill ? size_a : top_side_thickness));
const ImVec2 btl(mdy.x, mdy.y - (fill ? size_d : bottom_side_thickness));
const ImVec2 btr(mcy.x, mcy.y - (fill ? size_c : bottom_side_thickness));
const ImVec2 bbr = mcy;
const ImVec2 bbl = mdy;
const ImVec2 ltl = max;
const ImVec2 ltr(max.x + (fill ? size_a : left_side_thickness), max.y);
const ImVec2 lbr(mdx.x + (fill ? size_d : left_side_thickness), mdx.y);
const ImVec2 lbl = mdx;
const ImVec2 rtl(mbx.x - (fill ? size_b : right_side_thickness), mbx.y);
const ImVec2 rtr = mbx;
const ImVec2 rbr = mcx;
const ImVec2 rbl(mcx.x - (fill ? size_c : right_side_thickness), mcx.y);
// Reserve enough space for the vertices/indices
const int vtcs = fill ? (4 * 9) : (4 * 8);
const int idcs = fill ? (6 * 9) : (6 * 8);
draw_list->PrimReserve(idcs, vtcs);
const ImDrawIdx idx = (ImDrawIdx)draw_list->_VtxCurrentIdx;
// Snap a position to the nearest pixel to ensure correct rasterisation
#define SNAP_POS(vec) (ImVec2(ImTrunc((vec).x + 0.5f), ImTrunc((vec).y + 0.5f)))
// Write a corner vertex to the draw list, with d being the vertex index,
// p the position, c is the corner and i the index into the UV list
#define VTX_WRITE_CORNER(d, p, c, i) \
draw_list->_VtxWritePtr[d].pos = SNAP_POS(p); \
draw_list->_VtxWritePtr[d].uv = corner_uv_##c[(i)]; \
draw_list->_VtxWritePtr[d].col = col
// Write a vertex for one of the edge sections, with d being the vertex index,
// p the position, e is the edge (t/l/b/r) and i the index into the UV list
#define VTX_WRITE_EDGE(d, p, e, i) \
draw_list->_VtxWritePtr[d].pos = SNAP_POS(p); \
draw_list->_VtxWritePtr[d].uv = edge_uv_##e[(i)]; \
draw_list->_VtxWritePtr[d].col = col
// Write a vertex for the center fill, with d being the vertex index and
// p the position
#define VTX_WRITE_FILL(d, p) \
draw_list->_VtxWritePtr[d].pos = SNAP_POS(p); \
draw_list->_VtxWritePtr[d].uv = round_corner_uv[0]; \
draw_list->_VtxWritePtr[d].col = col
int dv = 0; // A count of the number of vertices we've written
int di = 0; // The number of indices we have written
// Write a triangle using three indices
#define IDX_WRITE_TRI(idx0, idx1, idx2) \
draw_list->_IdxWritePtr[di] = (ImDrawIdx)(idx+(idx0)); \
draw_list->_IdxWritePtr[di+1] = (ImDrawIdx)(idx+(idx1)); \
draw_list->_IdxWritePtr[di+2] = (ImDrawIdx)(idx+(idx2)); \
di += 3
// Top-left corner
{
VTX_WRITE_CORNER(dv + 0, ca, a, 2);
VTX_WRITE_CORNER(dv + 1, may, a, 1);
VTX_WRITE_CORNER(dv + 2, ia, a, 0);
VTX_WRITE_CORNER(dv + 3, max, a, 1);
IDX_WRITE_TRI(dv + 0, dv + 1, dv + 2);
IDX_WRITE_TRI(dv + 0, dv + 2, dv + 3);
dv += 4;
}
// Top-right corner
{
VTX_WRITE_CORNER(dv + 0, cb, b, 2);
VTX_WRITE_CORNER(dv + 1, mbx, b, 1);
VTX_WRITE_CORNER(dv + 2, ib, b, 0);
VTX_WRITE_CORNER(dv + 3, mby, b, 1);
IDX_WRITE_TRI(dv + 0, dv + 1, dv + 2);
IDX_WRITE_TRI(dv + 0, dv + 2, dv + 3);
dv += 4;
}
// Bottom-right corner
{
VTX_WRITE_CORNER(dv + 0, ic, c, 0);
VTX_WRITE_CORNER(dv + 1, mcx, c, 1);
VTX_WRITE_CORNER(dv + 2, cc, c, 2);
VTX_WRITE_CORNER(dv + 3, mcy, c, 1);
IDX_WRITE_TRI(dv + 0, dv + 1, dv + 2);
IDX_WRITE_TRI(dv + 0, dv + 2, dv + 3);
dv += 4;
}
// Bottom-left corner
{
VTX_WRITE_CORNER(dv + 0, cd, d, 2);
VTX_WRITE_CORNER(dv + 1, mdx, d, 1);
VTX_WRITE_CORNER(dv + 2, id, d, 0);
VTX_WRITE_CORNER(dv + 3, mdy, d, 1);
IDX_WRITE_TRI(dv + 0, dv + 1, dv + 2);
IDX_WRITE_TRI(dv + 0, dv + 2, dv + 3);
dv += 4;
}
// Top edge
{
VTX_WRITE_EDGE(dv + 0, ttl, t, 1);
VTX_WRITE_EDGE(dv + 1, ttr, t, 1);
VTX_WRITE_EDGE(dv + 2, tbr, t, 0);
VTX_WRITE_EDGE(dv + 3, tbl, t, 0);
IDX_WRITE_TRI(dv + 0, dv + 1, dv + 2);
IDX_WRITE_TRI(dv + 0, dv + 2, dv + 3);
dv += 4;
}
// Right edge
{
VTX_WRITE_EDGE(dv + 0, rtl, r, 0);
VTX_WRITE_EDGE(dv + 1, rtr, r, 1);
VTX_WRITE_EDGE(dv + 2, rbr, r, 1);
VTX_WRITE_EDGE(dv + 3, rbl, r, 0);
IDX_WRITE_TRI(dv + 0, dv + 1, dv + 2);
IDX_WRITE_TRI(dv + 0, dv + 2, dv + 3);
dv += 4;
}
// Bottom edge
{
VTX_WRITE_EDGE(dv + 0, btl, b, 0);
VTX_WRITE_EDGE(dv + 1, btr, b, 0);
VTX_WRITE_EDGE(dv + 2, bbr, b, 1);
VTX_WRITE_EDGE(dv + 3, bbl, b, 1);
IDX_WRITE_TRI(dv + 0, dv + 1, dv + 2);
IDX_WRITE_TRI(dv + 0, dv + 2, dv + 3);
dv += 4;
}
// Left edge
{
VTX_WRITE_EDGE(dv + 0, ltl, l, 1);
VTX_WRITE_EDGE(dv + 1, ltr, l, 0);
VTX_WRITE_EDGE(dv + 2, lbr, l, 0);
VTX_WRITE_EDGE(dv + 3, lbl, l, 1);
IDX_WRITE_TRI(dv + 0, dv + 1, dv + 2);
IDX_WRITE_TRI(dv + 0, dv + 2, dv + 3);
dv += 4;
}
// Fill
if (fill)
{
VTX_WRITE_FILL(dv + 0, ia);
VTX_WRITE_FILL(dv + 1, ib);
VTX_WRITE_FILL(dv + 2, ic);
VTX_WRITE_FILL(dv + 3, id);
IDX_WRITE_TRI(dv + 0, dv + 1, dv + 2);
IDX_WRITE_TRI(dv + 0, dv + 2, dv + 3);
dv += 4;
}
draw_list->_VtxWritePtr += dv;
draw_list->_VtxCurrentIdx += dv;
draw_list->_IdxWritePtr += di;
IM_ASSERT_PARANOID(di == idcs);
IM_ASSERT_PARANOID(dv == vtcs);
// Return any unused vertices/indices
// (not required ATM as we always generate the right number)
//draw_list->PrimUnreserve(idcs - di, vtcs - dv);
#undef SNAP_POS
#undef IDX_WRITE_TRI
#undef VTX_WRITE_CORNER
#undef VTX_WRITE_EDGE
#undef VTX_WRITE_FILL
return true;
}
// p_min = upper-left, p_max = lower-right
// Note we don't render 1 pixels sized rectangles properly.
void ImDrawList::AddRect(const ImVec2& p_min, const ImVec2& p_max, ImU32 col, float rounding, ImDrawFlags flags, float thickness)
{
if ((col & IM_COL32_A_MASK) == 0)
return;
#if 1
flags = FixRectCornerFlags(flags);
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);
#endif
// Try to use fast path if we can
if (rounding > 0)
if (AddRoundCornerRect(this, p_min, p_max, col, rounding, thickness, flags, /* fill */ false))
return;
if (Flags & ImDrawListFlags_AntiAliasedLines)
PathRect(p_min + ImVec2(0.50f, 0.50f), p_max - ImVec2(0.50f, 0.50f), rounding, flags);
else
@ -1413,6 +1773,13 @@ void ImDrawList::AddRectFilled(const ImVec2& p_min, const ImVec2& p_max, ImU32 c
{
if ((col & IM_COL32_A_MASK) == 0)
return;
#if 1
flags = FixRectCornerFlags(flags);
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);
#endif
if (rounding < 0.5f || (flags & ImDrawFlags_RoundCornersMask_) == ImDrawFlags_RoundCornersNone)
{
PrimReserve(6, 4);
@ -1420,6 +1787,10 @@ void ImDrawList::AddRectFilled(const ImVec2& p_min, const ImVec2& p_max, ImU32 c
}
else
{
// Try fast path first
if (AddRoundCornerRect(this, p_min, p_max, col, rounding, 1.0f, flags, /* fill */ true))
return;
PathRect(p_min, p_max, rounding, flags);
PathFillConvex(col);
}
@ -1487,11 +1858,196 @@ void ImDrawList::AddTriangleFilled(const ImVec2& p1, const ImVec2& p2, const ImV
PathFillConvex(col);
}
// Draw a circle using the rounded corner textures
// Returns true if the circle was drawn, or false if for some reason it could not be
// (in which case the caller should try the regular circle drawing code)
inline bool AddRoundCornerCircle(ImDrawList* draw_list, const ImVec2& center, float radius, float thickness, ImU32 col, bool fill)
{
if (!(draw_list->Flags & ImDrawListFlags_RoundCornersUseTex)) // Disabled by the draw list flags
return false;
const ImDrawListSharedData* data = draw_list->_Data;
IM_ASSERT(data->Font->ContainerAtlas->TexID == draw_list->_TextureIdStack.back()); // Use high-level ImGui::PushFont() or low-level ImDrawList::PushTextureId() to change font.
IM_ASSERT_PARANOID(!(data->Font->ContainerAtlas->Flags & ImFontAtlasFlags_NoBakedRoundCorners)); // No data in font
// Filled rectangles have no stroke width
const int stroke_width = fill ? 1 : (int)thickness;
if ((stroke_width <= 0) ||
(stroke_width > ImFontAtlasRoundCornersMaxStrokeWidth))
return false; // We can't handle this
// If we have a >1 stroke width, we actually need to increase the radius appropriately as well to match how the geometry renderer does things
const int rad = (int)radius + (stroke_width - 1);
// We don't support zero radius
if ((rad <= 0) || (rad > ImFontAtlasRoundCornersMaxSize))
return false; // We can't handle this
const unsigned int index = (stroke_width - 1) + ((rad - 1) * ImFontAtlasRoundCornersMaxStrokeWidth);
ImFontRoundedCornerData& round_corner_data = (*data->TexRoundCornerData)[index];
if (round_corner_data.RectId < 0)
return false; // No data for this configuration
// Calculate UVs for the three points we are interested in from the texture
// corner_uv[0] is the innermost point of the circle (solid for filled circles)
// corner_uv[1] is either straight down or across from it (depending on if we are using the filled or stroked version)
// corner_uv[2] is diagonally across from it
// corner_uv[1] is always solid (either inside the circle or on the line), whilst corner_uv[2] is always blank
// This represents a 45 degree "wedge" of circle, which then gets mirrored here to produce a 90 degree curve
// See ImFontAtlasBuildRenderRoundCornersTexData() for more details of the texture contents
// If use_alternative_uvs is true then this means we are drawing a stroked texture that has been packed into the "filled"
// corner of the rectangle, so we need to calculate UVs appropriately
const ImVec4& uvs = fill ? round_corner_data.TexUvFilled : round_corner_data.TexUvStroked;
const bool use_alternative_uvs = fill | round_corner_data.StrokedUsesAlternateUVs;
const ImVec2 corner_uv[3] =
{
ImVec2(uvs.x, uvs.y),
use_alternative_uvs ? ImVec2(uvs.x, uvs.w) : ImVec2(uvs.z, uvs.y),
ImVec2(uvs.z, uvs.w)
};
// Calculate the circle bounds
const ImVec2& c = center;
ImVec2 tl = ImVec2(c.x - rad, c.y - rad);
ImVec2 br = ImVec2(c.x + rad, c.y + rad);
// Some useful constants for our calculations
const float half_sqrt_two = 0.70710678f; // sqrtf(2.0f) * 0.5f
const float width_offset_parametric = round_corner_data.ParametricStrokeWidth; // Stroke width in our parametric coordinate space
const int num_verts = fill ? 9 : 16; // Number of vertices we are going to write
const int num_indices = fill ? 24 : 48; // Number of indices we are going to write
draw_list->PrimReserve(num_indices, num_verts);
// Write a vertex
// - d is the vertex index to write to
// - vert_pos is the vertex position
// - uv_coord is the UV coordinate
#define VTX_WRITE(d, vert_pos, uv_coord) \
draw_list->_VtxWritePtr[d].pos = vert_pos; \
draw_list->_VtxWritePtr[d].uv = uv_coord; \
draw_list->_VtxWritePtr[d].col = col
// Edge vertices working around the circle clockwise from the left
VTX_WRITE(0, ImVec2(tl.x, c.y), corner_uv[1]);
VTX_WRITE(1, tl, corner_uv[2]);
VTX_WRITE(2, ImVec2(c.x, tl.y), corner_uv[1]);
VTX_WRITE(3, ImVec2(br.x, tl.y), corner_uv[2]);
VTX_WRITE(4, ImVec2(br.x, c.y), corner_uv[1]);
VTX_WRITE(5, br, corner_uv[2]);
VTX_WRITE(6, ImVec2(c.x, br.y), corner_uv[1]);
VTX_WRITE(7, ImVec2(tl.x, br.y), corner_uv[2]);
if (fill)
{
// The center
VTX_WRITE(8, c, corner_uv[0]);
}
else
{
// Inside vertices on the diagonals of each quadrant
const ImVec2 tlbi = ImVec2(ImLerp(c.x, tl.x, half_sqrt_two - width_offset_parametric), ImLerp(c.y, tl.y, half_sqrt_two - width_offset_parametric));
const ImVec2 trbi = ImVec2(ImLerp(c.x, br.x, half_sqrt_two - width_offset_parametric), ImLerp(c.y, tl.y, half_sqrt_two - width_offset_parametric));
const ImVec2 brbi = ImVec2(ImLerp(c.x, br.x, half_sqrt_two - width_offset_parametric), ImLerp(c.y, br.y, half_sqrt_two - width_offset_parametric));
const ImVec2 blbi = ImVec2(ImLerp(c.x, tl.x, half_sqrt_two - width_offset_parametric), ImLerp(c.y, br.y, half_sqrt_two - width_offset_parametric));
// UV for the inside diagonal points
ImVec2 uvbi = ImVec2(ImLerp(corner_uv[0].x, corner_uv[2].x, half_sqrt_two - width_offset_parametric), ImLerp(corner_uv[0].y, corner_uv[2].y, half_sqrt_two - width_offset_parametric));
// Left/right/top/bottom interior positions
const ImVec2 lbi = ImVec2(ImLerp(tl.x, c.x, width_offset_parametric), c.y);
const ImVec2 rbi = ImVec2(ImLerp(br.x, c.x, width_offset_parametric), c.y);
const ImVec2 tbi = ImVec2(c.x, ImLerp(tl.y, c.y, width_offset_parametric));
const ImVec2 bbi = ImVec2(c.x, ImLerp(br.y, c.y, width_offset_parametric));
// UV for the interior cardinal points
ImVec2 uvi_cardinal = use_alternative_uvs ?
ImVec2(corner_uv[0].x, ImLerp(corner_uv[2].y, corner_uv[0].y, width_offset_parametric)) :
ImVec2(ImLerp(corner_uv[2].x, corner_uv[0].x, width_offset_parametric), corner_uv[0].y);
// Inner vertices, starting from the left
VTX_WRITE(8, lbi, uvi_cardinal);
VTX_WRITE(9, tlbi, uvbi);
VTX_WRITE(10, tbi, uvi_cardinal);
VTX_WRITE(11, trbi, uvbi);
VTX_WRITE(12, rbi, uvi_cardinal);
VTX_WRITE(13, brbi, uvbi);
VTX_WRITE(14, bbi, uvi_cardinal);
VTX_WRITE(15, blbi, uvbi);
}
// Write indices for a triangle formed of three indices
// d is the array index to write to
#define IDX_WRITE_TRI(d, idx0, idx1, idx2) \
draw_list->_IdxWritePtr[d+0] = (ImDrawIdx)(idx+idx0); \
draw_list->_IdxWritePtr[d+1] = (ImDrawIdx)(idx+idx1); \
draw_list->_IdxWritePtr[d+2] = (ImDrawIdx)(idx+idx2)
ImDrawIdx idx = (ImDrawIdx)draw_list->_VtxCurrentIdx;
if (fill)
{
// A simple fan of tris from the center
IDX_WRITE_TRI(0, 8, 0, 1);
IDX_WRITE_TRI(3, 8, 1, 2);
IDX_WRITE_TRI(6, 8, 2, 3);
IDX_WRITE_TRI(9, 8, 3, 4);
IDX_WRITE_TRI(12, 8, 4, 5);
IDX_WRITE_TRI(15, 8, 5, 6);
IDX_WRITE_TRI(18, 8, 6, 7);
IDX_WRITE_TRI(21, 8, 7, 0);
}
else
{
// A ring of inner vertices that are tight to the circle, spanning out to the four corners
// Top-left quadrant
IDX_WRITE_TRI(0, 1, 0, 8);
IDX_WRITE_TRI(3, 1, 8, 9);
IDX_WRITE_TRI(6, 1, 9, 10);
IDX_WRITE_TRI(9, 1, 10, 2);
// Top-right quadrant
IDX_WRITE_TRI(12, 3, 2, 10);
IDX_WRITE_TRI(15, 3, 10, 11);
IDX_WRITE_TRI(18, 3, 11, 12);
IDX_WRITE_TRI(21, 3, 12, 4);
// Bottom-right quadrant
IDX_WRITE_TRI(24, 5, 4, 12);
IDX_WRITE_TRI(27, 5, 12, 13);
IDX_WRITE_TRI(30, 5, 13, 14);
IDX_WRITE_TRI(33, 5, 14, 6);
// Bottom-left quadrant
IDX_WRITE_TRI(36, 7, 6, 14);
IDX_WRITE_TRI(39, 7, 14, 15);
IDX_WRITE_TRI(42, 7, 15, 8);
IDX_WRITE_TRI(45, 7, 8, 0);
}
draw_list->_VtxWritePtr += num_verts;
draw_list->_VtxCurrentIdx += num_verts;
draw_list->_IdxWritePtr += num_indices;
#undef IDX_WRITE_TRI
#undef VTX_WRITE
return true;
}
void ImDrawList::AddCircle(const ImVec2& center, float radius, ImU32 col, int num_segments, float thickness)
{
if ((col & IM_COL32_A_MASK) == 0 || radius < 0.5f)
return;
// First try the fast texture-based renderer, and only if that can't handle this fall back to paths
if (AddRoundCornerCircle(this, center, radius, thickness, col, false))
return;
// Obtain segment count
if (num_segments <= 0)
{
// Use arc with automatic segment count
@ -1516,6 +2072,10 @@ void ImDrawList::AddCircleFilled(const ImVec2& center, float radius, ImU32 col,
if ((col & IM_COL32_A_MASK) == 0 || radius < 0.5f)
return;
// First try the fast texture-based renderer, and only if that can't handle this fall back to paths
if (AddRoundCornerCircle(this, center, radius, 1.0f, col, true))
return;
if (num_segments <= 0)
{
// Use arc with automatic segment count
@ -1538,7 +2098,7 @@ 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)
{
if ((col & IM_COL32_A_MASK) == 0 || num_segments <= 2)
if ((col & IM_COL32_A_MASK) == 0 || (num_segments <= 2) || (radius <= 0.0f))
return;
// Because we are filling a closed shape we remove 1 from the count of segments/points
@ -1550,7 +2110,7 @@ void ImDrawList::AddNgon(const ImVec2& center, float radius, ImU32 col, int num_
// Guaranteed to honor 'num_segments'
void ImDrawList::AddNgonFilled(const ImVec2& center, float radius, ImU32 col, int num_segments)
{
if ((col & IM_COL32_A_MASK) == 0 || num_segments <= 2)
if ((col & IM_COL32_A_MASK) == 0 || (num_segments <= 2) || (radius <= 0.0f))
return;
// Because we are filling a closed shape we remove 1 from the count of segments/points
@ -2413,6 +2973,7 @@ void ImFontAtlas::ClearInputData()
ConfigData.clear();
CustomRects.clear();
PackIdMouseCursors = PackIdLines = -1;
TexRoundCornerData.clear();
// Important: we leave TexReady untouched
}
@ -3193,6 +3754,8 @@ static void ImFontAtlasBuildRenderLinesTexData(ImFontAtlas* atlas)
}
}
static void ImFontAtlasBuildRegisterRoundCornersCustomRects(ImFontAtlas* atlas);
// Note: this is called / shared by both the stb_truetype and the FreeType builder
void ImFontAtlasBuildInit(ImFontAtlas* atlas)
{
@ -3219,6 +3782,225 @@ void ImFontAtlasBuildInit(ImFontAtlas* atlas)
if (!(atlas->Flags & ImFontAtlasFlags_NoBakedLines))
atlas->PackIdLines = atlas->AddCustomRectRegular(IM_DRAWLIST_TEX_LINES_WIDTH_MAX + 2, IM_DRAWLIST_TEX_LINES_WIDTH_MAX + 1);
}
ImFontAtlasBuildRegisterRoundCornersCustomRects(atlas);
}
const int FONT_ATLAS_ROUNDED_CORNER_TEX_PADDING = 2;
const int FONT_ATLAS_ROUNDED_CORNER_TEX_CENTER_PADDING = 4; // Padding applied to the Y axis to separate the two halves of the image (this must be a multiple of two)
// Register the rectangles we need for the rounded corner images
static void ImFontAtlasBuildRegisterRoundCornersCustomRects(ImFontAtlas* atlas)
{
if (atlas->TexRoundCornerData.Size > 0)
return;
if (atlas->Flags & ImFontAtlasFlags_NoBakedRoundCorners)
return;
const int pad = FONT_ATLAS_ROUNDED_CORNER_TEX_PADDING;
const unsigned int max_radius = ImFontAtlasRoundCornersMaxSize;
const unsigned int max_thickness = ImFontAtlasRoundCornersMaxStrokeWidth;
atlas->TexRoundCornerData.reserve(max_radius * max_thickness * 2);
// We do this twice, one for rounded corners and once for square corners
for (int square = 0; square < 2; square++) // 1 for square corners
{
for (unsigned int radius_index = 0; radius_index < max_radius; radius_index++)
{
int spare_rect_id = -1; // The last rectangle ID we generated with a spare half
for (unsigned int stroke_width_index = 0; stroke_width_index < max_thickness; stroke_width_index++)
{
const int width = radius_index + 1 + pad * 2;
const int height = radius_index + 1 + FONT_ATLAS_ROUNDED_CORNER_TEX_CENTER_PADDING + pad * 2;
ImFontRoundedCornerData corner_data;
if ((ImFontAtlasRoundCornersStrokeWidthMask & (1 << stroke_width_index)) && (ImFontAtlasRoundCornersSizeMask & (1ULL << radius_index)))
{
if (stroke_width_index == 0 || spare_rect_id < 0)
{
corner_data.RectId = atlas->AddCustomRectRegular(width, height);
corner_data.StrokedUsesAlternateUVs = false;
if (stroke_width_index != 0)
spare_rect_id = corner_data.RectId;
}
else
{
// Pack this into the spare half of the previous rect
corner_data.RectId = spare_rect_id;
corner_data.StrokedUsesAlternateUVs = true;
spare_rect_id = -1;
}
}
else
{
corner_data.RectId = -1; // Set RectId to -1 if we don't want any data
}
ImVector<ImFontRoundedCornerData>& data_list = square ? atlas->TexSquareCornerData : atlas->TexRoundCornerData;
IM_ASSERT_PARANOID(data_list.Size == (int)index);
data_list.push_back(corner_data);
}
}
}
}
// Generate the actual pixel data for rounded corners in the atlas
static void ImFontAtlasBuildRenderRoundCornersTexData(ImFontAtlas* atlas)
{
IM_ASSERT(atlas->TexPixelsAlpha8 != NULL);
if (atlas->Flags & ImFontAtlasFlags_NoBakedRoundCorners)
return;
// Render the texture
const int w = atlas->TexWidth;
const unsigned int max = ImFontAtlasRoundCornersMaxSize * ImFontAtlasRoundCornersMaxStrokeWidth;
const int pad = FONT_ATLAS_ROUNDED_CORNER_TEX_PADDING;
IM_UNUSED(max);
IM_ASSERT(atlas->TexRoundCornerData.Size == (int)max); // ImFontAtlasBuildRegisterRoundCornersCustomRects() will have created these for us
IM_ASSERT(atlas->TexSquareCornerData.Size == (int)max);
const unsigned int max_radius = ImFontAtlasRoundCornersMaxSize;
const unsigned int max_thickness = ImFontAtlasRoundCornersMaxStrokeWidth;
for (int square = 0; square < 2; square++) // 1 = Square corners instead of round
{
ImVector<ImFontRoundedCornerData>& data_list = square ? atlas->TexSquareCornerData : atlas->TexRoundCornerData;
data_list.reserve(max_radius * max_thickness);
for (unsigned int radius_index = 0; radius_index < max_radius; radius_index++)
for (unsigned int stroke_width_index = 0; stroke_width_index < max_thickness; stroke_width_index++)
{
const unsigned int index = stroke_width_index + (radius_index * ImFontAtlasRoundCornersMaxStrokeWidth);
const unsigned int radius = radius_index + 1;
const float stroke_width = (float)stroke_width_index + 1;
ImFontRoundedCornerData& data = data_list[index];
if (data.RectId < 0)
continue; // We don't want to generate data for this
ImFontAtlasCustomRect& r = atlas->CustomRects[data.RectId];
IM_ASSERT(r.IsPacked());
IM_ASSERT(r.Width == radius + pad * 2 && r.Height == radius + FONT_ATLAS_ROUNDED_CORNER_TEX_CENTER_PADDING + pad * 2);
// If we are generating data for a stroke width > 0, then look for another stroke width sharing this rectangle
float other_stroke_width = -1.0f;
ImFontRoundedCornerData* other_data = NULL;
if (stroke_width_index > 0)
{
// We use the fact that we know shared pairs will always appear together to both make this check fast and skip trying
// to generate the second half of the pair again when the main loop comes around
stroke_width_index++;
while (stroke_width_index < max_thickness)
{
const unsigned int candidate_index = stroke_width_index + (radius_index * ImFontAtlasRoundCornersMaxStrokeWidth);
ImFontRoundedCornerData* candidate_data = &data_list[candidate_index];
if (candidate_data->RectId == data.RectId)
{
other_data = candidate_data;
other_stroke_width = (float)stroke_width_index + 1;
other_data->ParametricStrokeWidth = ((other_stroke_width > 1.0f) ? (other_stroke_width + 2.0f) : other_stroke_width) / (float)radius;
break;
}
stroke_width_index++;
}
}
// What we're doing here is generating a rectangular image that contains the data for both the filled and
// stroked variants of the corner with the radius specified. We do it like this because we only need 45 degrees
// worth of curve (as each corner mirrors the texture to get the full 90 degrees), and hence with a little care
// we can put both variants into one texture by using two triangular regions. In practice this is a little more
// tricky than it first looks because if the two regions are packed tightly you get filtering errors where they meet,
// so we offset one vertically from the other by FONT_ATLAS_ROUNDED_CORNER_TEX_CENTER_PADDING pixels.
// The stroked version is at the top-right of the texture, and the filled version at the bottom-left.
// Pre-calculate the parametric stroke width (+2 to give space for texture filtering on non-single-pixel widths)
data.ParametricStrokeWidth = ((stroke_width > 1.0f) ? (stroke_width + 2.0f) : stroke_width) / (float)radius;
for (int y = -pad; y < (int)(radius + FONT_ATLAS_ROUNDED_CORNER_TEX_CENTER_PADDING + pad); y++)
for (int x = -pad; x < (int)(radius); x++)
{
// We want the pad area to essentially contain a clamped version of the 0th row/column, so
// clamp here. Not doing this results in nasty filtering artifacts at low radii.
const float sampling_offset = 0.25f; // Experimentally obtained offset to compensate for sampling point
float cx = ImMax(x + sampling_offset, 0.0f);
float cy = ImMax(y + sampling_offset, 0.0f);
// The X<Y region of the texture contains the data for filled corners, the X>Y region
// the data for stroked ones. We add half of FONT_ATLAS_ROUNDED_CORNER_TEX_CENTER_PADDING so that
// each side gets a buffer zone to avoid filtering artifacts.
// For stroke widths > 1, we use the "filled" area to hold a second stroke width variant
const bool filled = x < (y - (FONT_ATLAS_ROUNDED_CORNER_TEX_CENTER_PADDING >> 1));
if (filled)
{
// The filled version starts a little further down the texture to give us the padding in the middle.
cy = ImMax((float)y - FONT_ATLAS_ROUNDED_CORNER_TEX_CENTER_PADDING, 0.0f);
}
const float dist = (square ? ImMax(cx, cy) : ImSqrt((float)(cx*cx + cy * cy))) - (float)(radius - (filled ? 0 : (stroke_width * 0.5f) + 0.5f));
float alpha = 0.0f;
if (filled)
{
if (stroke_width_index > 0)
{
if (other_data)
{
// Using the filled section to hold a second stroke width variant instead of filled if we are at a stroke width > 1
const float other_dist = (square ? ImMax(cx, cy) : ImSqrt((float)(cx*cx + cy*cy))) - (float)(radius - ((other_stroke_width * 0.5f) + 0.5f));
const float alpha1 = ImClamp(other_dist + other_stroke_width, 0.0f, 1.0f);
const float alpha2 = ImClamp(other_dist, 0.0f, 1.0f);
alpha = alpha1 - alpha2;
}
}
else
{
alpha = ImClamp(-dist, 0.0f, 1.0f); // Filled version
}
}
else
{
const float alpha1 = ImClamp(dist + stroke_width, 0.0f, 1.0f);
const float alpha2 = ImClamp(dist, 0.0f, 1.0f);
alpha = alpha1 - alpha2;
}
const unsigned int offset = (int)(r.X + pad + x) + (int)(r.Y + pad + y) * w;
atlas->TexPixelsAlpha8[offset] = (unsigned char)(0xFF * ImSaturate(alpha));
}
// We generate two sets of UVs for each rectangle, one for the filled portion and one for the unfilled bit.
for (unsigned int stage = 0; stage < 2; stage++)
{
ImFontAtlasCustomRect stage_rect = r;
const bool filled = (stage == 0);
stage_rect.X += pad;
stage_rect.Y += pad + (filled ? FONT_ATLAS_ROUNDED_CORNER_TEX_CENTER_PADDING : 0);
stage_rect.Width -= (pad * 2);
stage_rect.Height -= (pad * 2) + FONT_ATLAS_ROUNDED_CORNER_TEX_CENTER_PADDING;
ImVec2 uv0, uv1;
atlas->CalcCustomRectUV(&stage_rect, &uv0, &uv1);
if (stage == 0)
{
if (other_data)
other_data->TexUvStroked = ImVec4(uv0.x, uv0.y, uv1.x, uv1.y);
else
data.TexUvFilled = ImVec4(uv0.x, uv0.y, uv1.x, uv1.y);
}
else
{
data.TexUvStroked = ImVec4(uv0.x, uv0.y, uv1.x, uv1.y);
}
}
}
}
}
// This is called/shared by both the stb_truetype and the FreeType builder.
@ -3229,6 +4011,9 @@ void ImFontAtlasBuildFinish(ImFontAtlas* atlas)
ImFontAtlasBuildRenderDefaultTexData(atlas);
ImFontAtlasBuildRenderLinesTexData(atlas);
// Render into our rounded corner data block
ImFontAtlasBuildRenderRoundCornersTexData(atlas);
// Register custom rectangle glyphs
for (int i = 0; i < atlas->CustomRects.Size; i++)
{
@ -4199,6 +4984,7 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, Im
// - RenderArrowPointingAt()
// - RenderRectFilledRangeH()
// - RenderRectFilledWithHole()
// - RenderWindowResizeGrip()
//-----------------------------------------------------------------------------
// Function in need of a redesign (legacy mess)
// - RenderColorRectWithAlphaCheckerboard()
@ -4355,6 +5141,93 @@ void ImGui::RenderRectFilledWithHole(ImDrawList* draw_list, const ImRect& outer,
if (fill_R && fill_D) draw_list->AddRectFilled(ImVec2(inner.Max.x, inner.Max.y), ImVec2(outer.Max.x, outer.Max.y), col, rounding, ImDrawFlags_RoundCornersBottomRight);
}
// Add a resize grip using the rounded corner textures, if possible.
// Returns false if rendering could not be performed, true otherwise
// FIXME: Probably ok to move this to imgui_draw.cpp in 'Internal Render Helpers' section.
bool ImGui::RenderWindowResizeGrip(ImDrawList* draw_list, const ImVec2& corner, unsigned int rad, unsigned int overall_grip_size, ImDrawFlags flags, ImU32 col)
{
// Texture path disabled by the draw list flags
// Texture path disabled for radius 0 which cause issues with the UV lookup below
const bool use_tex = (draw_list->Flags & ImDrawListFlags_RoundCornersUseTex) && (rad >= 1 && rad <= ImFontAtlasRoundCornersMaxSize);
if (use_tex == false)
return false;
ImFontAtlas* atlas = draw_list->_Data->Font->ContainerAtlas;
IM_UNUSED(atlas);
IM_ASSERT(atlas->TexID == draw_list->_TextureIdStack.back()); // Use high-level ImGui::PushFont() or low-level ImDrawList::PushTextureId() to change font.
IM_ASSERT(ImIsPowerOfTwo(flags)); // Only allow a single corner to be specified here.
IM_ASSERT_PARANOID((atlas->Flags & ImFontAtlasFlags_NoBakedRoundCorners) == 0);
// Calculate UVs for the three points we are interested in from the texture
// - uv[0] is the mid-point from the corner towards the center of the circle (solid)
// - uv[1] is a solid point on the edge of the circle
// - uv[2] is the outer edge (blank, outside the circle)
const unsigned int index = (rad - 1) * ImFontAtlasRoundCornersMaxStrokeWidth;
const ImVec4& uvs = (*draw_list->_Data->TexRoundCornerData)[index].TexUvFilled;
const ImVec2 uv[] =
{
ImVec2(ImLerp(uvs.x, uvs.z, 0.5f), ImLerp(uvs.y, uvs.w, 0.5f)),
ImVec2(uvs.x, uvs.y),
ImVec2(uvs.z, uvs.w),
};
// Calculate the coordinates of the points at the inside of the rounded area of the corner, and the outside of the grip on the X/Y axes
ImVec2 in_x = corner, in_y = corner, out_x = corner, out_y = corner;
if (flags & (ImDrawFlags_RoundCornersTopLeft | ImDrawFlags_RoundCornersTopRight))
{
in_y.y += rad;
out_y.y += overall_grip_size;
}
else if (flags & (ImDrawFlags_RoundCornersBottomLeft | ImDrawFlags_RoundCornersBottomRight))
{
in_y.y -= rad;
out_y.y -= overall_grip_size;
}
if (flags & (ImDrawFlags_RoundCornersTopLeft | ImDrawFlags_RoundCornersBottomLeft))
{
in_x.x += rad;
out_x.x += overall_grip_size;
}
else if (flags & (ImDrawFlags_RoundCornersTopRight | ImDrawFlags_RoundCornersBottomRight))
{
in_x.x -= rad;
out_x.x -= overall_grip_size;
}
// Calculate the mid-point on the diagonal
const ImVec2 mid = ImVec2(ImLerp(in_x.x, in_y.x, 0.5f), ImLerp(in_x.y, in_y.y, 0.5f));
// Now write out the geometry
const int num_verts = 6; // Number of vertices we are going to write
const int num_indices = 12; // Number of indices we are going to write
draw_list->PrimReserve(num_indices, num_verts);
unsigned int idx = draw_list->_VtxCurrentIdx;
ImDrawVert* vtx_write_ptr = draw_list->_VtxWritePtr;
ImDrawIdx* idx_write_ptr = draw_list->_IdxWritePtr;
vtx_write_ptr[0].pos = mid; vtx_write_ptr[0].uv = uv[0]; vtx_write_ptr[0].col = col;
vtx_write_ptr[1].pos = in_y; vtx_write_ptr[1].uv = uv[1]; vtx_write_ptr[1].col = col;
vtx_write_ptr[2].pos = corner; vtx_write_ptr[2].uv = uv[2]; vtx_write_ptr[2].col = col;
vtx_write_ptr[3].pos = in_x; vtx_write_ptr[3].uv = uv[1]; vtx_write_ptr[3].col = col;
vtx_write_ptr[4].pos = out_x; vtx_write_ptr[4].uv = uv[1]; vtx_write_ptr[4].col = col;
vtx_write_ptr[5].pos = out_y; vtx_write_ptr[5].uv = uv[1]; vtx_write_ptr[5].col = col;
// Curved section
idx_write_ptr[0] = (ImDrawIdx)(idx); idx_write_ptr[1] = (ImDrawIdx)(idx + 1); idx_write_ptr[2] = (ImDrawIdx)(idx + 2);
idx_write_ptr[3] = (ImDrawIdx)(idx); idx_write_ptr[4] = (ImDrawIdx)(idx + 2); idx_write_ptr[5] = (ImDrawIdx)(idx + 3);
// Outer section
idx_write_ptr[6] = (ImDrawIdx)(idx + 4); idx_write_ptr[7] = (ImDrawIdx)(idx + 3); idx_write_ptr[8] = (ImDrawIdx)(idx + 5);
idx_write_ptr[9] = (ImDrawIdx)(idx + 3); idx_write_ptr[10] = (ImDrawIdx)(idx + 5); idx_write_ptr[11] = (ImDrawIdx)(idx + 1);
draw_list->_VtxWritePtr += num_verts;
draw_list->_VtxCurrentIdx += num_verts;
draw_list->_IdxWritePtr += num_indices;
return true;
}
// Helper for ColorPicker4()
// NB: This is rather brittle and will show artifact when rounding this enabled if rounded corners overlap multiple cells. Caller currently responsible for avoiding that.
// Spent a non reasonable amount of time trying to getting this right for ColorButton with rounding+anti-aliasing+ImGuiColorEditFlags_HalfAlphaPreview flag + various grid sizes and offsets, and eventually gave up... probably more reasonable to disable rounding altogether.
@ -4396,6 +5269,7 @@ void ImGui::RenderColorRectWithAlphaCheckerboard(ImDrawList* draw_list, ImVec2 p
}
}
//-----------------------------------------------------------------------------
// [SECTION] Decompression code
//-----------------------------------------------------------------------------

@ -786,6 +786,9 @@ struct IMGUI_API ImDrawListSharedData
ImU8 CircleSegmentCounts[64]; // Precomputed segment count for given radius before we calculate it dynamically (to avoid calculation overhead)
const ImVec4* TexUvLines; // UV of anti-aliased lines in the atlas
ImVector<ImFontRoundedCornerData>* TexRoundCornerData; // Data for texture-based rounded corners, indexed by radius
ImVector<ImFontRoundedCornerData>* TexSquareCornerData; // Data for texture-based square corners, indexed by radius
ImDrawListSharedData();
void SetCircleTessellationMaxError(float max_error);
};
@ -3402,6 +3405,7 @@ namespace ImGui
IMGUI_API void RenderBullet(ImDrawList* draw_list, ImVec2 pos, ImU32 col);
IMGUI_API void RenderCheckMark(ImDrawList* draw_list, ImVec2 pos, ImU32 col, float sz);
IMGUI_API void RenderArrowPointingAt(ImDrawList* draw_list, ImVec2 pos, ImVec2 half_sz, ImGuiDir direction, ImU32 col);
IMGUI_API bool RenderWindowResizeGrip(ImDrawList* draw_list, const ImVec2& corner, unsigned int rad, unsigned int overall_grip_size, ImDrawFlags flags, ImU32 col);
IMGUI_API void RenderRectFilledRangeH(ImDrawList* draw_list, const ImRect& rect, ImU32 col, float x_start_norm, float x_end_norm, float rounding);
IMGUI_API void RenderRectFilledWithHole(ImDrawList* draw_list, const ImRect& outer, const ImRect& inner, ImU32 col, float rounding);
@ -3536,7 +3540,6 @@ namespace ImGui
} // namespace ImGui
//-----------------------------------------------------------------------------
// [SECTION] ImFontAtlas internal API
//-----------------------------------------------------------------------------
@ -3561,6 +3564,16 @@ IMGUI_API void ImFontAtlasBuildRender32bppRectFromString(ImFontAtlas* atlas
IMGUI_API void ImFontAtlasBuildMultiplyCalcLookupTable(unsigned char out_table[256], float in_multiply_factor);
IMGUI_API void ImFontAtlasBuildMultiplyRectAlpha8(const unsigned char table[256], unsigned char* pixels, int x, int y, int w, int h, int stride);
// Note that stroke width increases effective radius, so (e.g.) a max radius circle will have to use the fallback path if stroke width is > 1. Note that ImFontAtlasRoundCornersSizeMask is 64 bits so this value can only go up to a maximum of 64 at present.
const int ImFontAtlasRoundCornersMaxSize = 32; // Maximum size of rounded corner texture to generate in fonts
// Bit mask for which radii will have texture generated for them, starting from radius 1. Only bits up to ImFontAtlasRoundCornersMaxSize are considered.
const ImU64 ImFontAtlasRoundCornersSizeMask = (1ULL << ImFontAtlasRoundCornersMaxSize) - 1;
const int ImFontAtlasRoundCornersMaxStrokeWidth = 4; // Maximum stroke width of rounded corner texture to generate in fonts
// Bit mask for which stroke widths should have textures generated for them (the default of 0x0B means widths 1, 2 and 4)
// Only bits up to ImFontAtlasRoundCornersMaxStrokeWidth are considered, and bit 0 (stroke width 1) must always be set
// Optimally there should be an odd number of bits set, as the texture packing packs the data in pairs, with one half of one pair being occupied by the filled texture
const ImU32 ImFontAtlasRoundCornersStrokeWidthMask = 0x0B;
//-----------------------------------------------------------------------------
// [SECTION] Test Engine specific hooks (imgui_test_engine)
//-----------------------------------------------------------------------------

Loading…
Cancel
Save