mirror of
https://github.com/Drezil/imgui.git
synced 2025-07-13 08:19:55 +02:00
Compare commits
19 Commits
docking
...
features/t
Author | SHA1 | Date | |
---|---|---|---|
69c157e418 | |||
b175371325 | |||
75f5aaaa1c | |||
5820a7e248 | |||
a6fa56609b | |||
05d9502075 | |||
5947842934 | |||
f41e86c78e | |||
1858d8d3f5 | |||
ff3e7feba7 | |||
8e97a4a724 | |||
a0ef8c9187 | |||
3f671bd282 | |||
a1efd8fc06 | |||
ed1e598ebe | |||
edb797538d | |||
054c87fc4f | |||
d81c5cbae3 | |||
fbc6bce06b |
32
imgui.cpp
32
imgui.cpp
@ -1070,6 +1070,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.
|
||||
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.
|
||||
|
||||
@ -4297,6 +4298,8 @@ void ImGui::NewFrame()
|
||||
g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedFill;
|
||||
if (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset)
|
||||
g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AllowVtxOffset;
|
||||
if (g.Style.RoundCornersUseTex && !(g.Font->ContainerAtlas->Flags & ImFontAtlasFlags_NoBakedRoundCorners))
|
||||
g.DrawListSharedData.InitialFlags |= ImDrawListFlags_RoundCornersUseTex;
|
||||
|
||||
// Mark rendering data as invalid to prevent user who may have a handle on it to use it.
|
||||
for (int n = 0; n < g.Viewports.Size; n++)
|
||||
@ -5519,13 +5522,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
|
||||
@ -5815,10 +5819,20 @@ void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar
|
||||
{
|
||||
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(resize_grip_col[resize_grip_n]);
|
||||
|
||||
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, resize_grip_col[resize_grip_n]))
|
||||
{
|
||||
// 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(resize_grip_col[resize_grip_n]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -6837,6 +6851,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;
|
||||
}
|
||||
|
20
imgui.h
20
imgui.h
@ -1875,6 +1875,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. 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];
|
||||
@ -2484,7 +2485,8 @@ enum ImDrawListFlags_
|
||||
ImDrawListFlags_AntiAliasedLines = 1 << 0, // Enable anti-aliased lines/borders (*2 the number of triangles for 1.0f wide line or lines thin enough to be drawn using textures, otherwise *3 the number of triangles)
|
||||
ImDrawListFlags_AntiAliasedLinesUseTex = 1 << 1, // Enable anti-aliased lines/borders using textures when possible. Require backend to render with bilinear 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_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
|
||||
@ -2699,6 +2701,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
|
||||
{
|
||||
@ -2718,7 +2730,8 @@ enum ImFontAtlasFlags_
|
||||
ImFontAtlasFlags_None = 0,
|
||||
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). The AntiAliasedLinesUseTex features uses them, otherwise they will be rendered using polygons (more expensive for CPU/GPU).
|
||||
ImFontAtlasFlags_NoBakedLines = 1 << 2, // Don't build thick line textures into the atlas (save a little texture memory). 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:
|
||||
@ -2832,6 +2845,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+
|
||||
|
174
imgui_demo.cpp
174
imgui_demo.cpp
@ -240,6 +240,177 @@ void ImGui::ShowUserGuide()
|
||||
ImGui::Unindent();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
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()
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -409,6 +580,8 @@ void ImGui::ShowDemoWindow(bool* p_open)
|
||||
ImGui::Text("dear imgui says hello. (%s)", IMGUI_VERSION);
|
||||
ImGui::Spacing();
|
||||
|
||||
TestTextureBasedRender();
|
||||
|
||||
IMGUI_DEMO_MARKER("Help");
|
||||
if (ImGui::CollapsingHeader("Help"))
|
||||
{
|
||||
@ -6275,6 +6448,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;
|
||||
|
878
imgui_draw.cpp
878
imgui_draw.cpp
@ -1388,12 +1388,373 @@ 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
|
||||
|
||||
ImTextureID tex_id = data->Font->ContainerAtlas->TexID;
|
||||
IM_ASSERT(tex_id == 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(ImFloor((vec).x + 0.5f), ImFloor((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
|
||||
@ -1405,6 +1766,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.0f || (flags & ImDrawFlags_RoundCornersMask_) == ImDrawFlags_RoundCornersNone)
|
||||
{
|
||||
PrimReserve(6, 4);
|
||||
@ -1412,6 +1780,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);
|
||||
}
|
||||
@ -1479,11 +1851,197 @@ 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;
|
||||
ImTextureID tex_id = data->Font->ContainerAtlas->TexID;
|
||||
IM_ASSERT(tex_id == 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.0f)
|
||||
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
|
||||
@ -1508,6 +2066,10 @@ void ImDrawList::AddCircleFilled(const ImVec2& center, float radius, ImU32 col,
|
||||
if ((col & IM_COL32_A_MASK) == 0 || radius <= 0.0f)
|
||||
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
|
||||
@ -1530,7 +2092,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
|
||||
@ -1542,7 +2104,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
|
||||
@ -2005,6 +2567,7 @@ void ImFontAtlas::ClearInputData()
|
||||
ConfigData.clear();
|
||||
CustomRects.clear();
|
||||
PackIdMouseCursors = PackIdLines = -1;
|
||||
TexRoundCornerData.clear();
|
||||
// Important: we leave TexReady untouched
|
||||
}
|
||||
|
||||
@ -2755,6 +3318,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)
|
||||
{
|
||||
@ -2774,6 +3339,224 @@ 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_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.
|
||||
@ -2784,6 +3567,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++)
|
||||
{
|
||||
@ -3739,6 +4525,7 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col
|
||||
// - RenderArrowPointingAt()
|
||||
// - RenderRectFilledRangeH()
|
||||
// - RenderRectFilledWithHole()
|
||||
// - RenderWindowResizeGrip()
|
||||
//-----------------------------------------------------------------------------
|
||||
// Function in need of a redesign (legacy mess)
|
||||
// - RenderColorRectWithAlphaCheckerboard()
|
||||
@ -3894,6 +4681,92 @@ void ImGui::RenderRectFilledWithHole(ImDrawList* draw_list, ImRect outer, ImRect
|
||||
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_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.
|
||||
@ -3935,6 +4808,7 @@ void ImGui::RenderColorRectWithAlphaCheckerboard(ImDrawList* draw_list, ImVec2 p
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// [SECTION] Decompression code
|
||||
//-----------------------------------------------------------------------------
|
||||
|
@ -725,6 +725,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);
|
||||
};
|
||||
@ -2770,6 +2773,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, ImRect outer, ImRect inner, ImU32 col, float rounding);
|
||||
|
||||
@ -2872,7 +2876,6 @@ namespace ImGui
|
||||
|
||||
} // namespace ImGui
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// [SECTION] ImFontAtlas internal API
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -2896,6 +2899,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)
|
||||
//-----------------------------------------------------------------------------
|
||||
|
Reference in New Issue
Block a user