mirror of
https://github.com/Drezil/imgui.git
synced 2024-11-22 20:07:01 +00:00
Texture-based round corners: Refactoring and upgrades
Changed texture-based rounded rectangle edges to draw using thin polygons Rearranged data for texture-based rounded rectangles to reduce allocations and pre-calculate parametric coordinate Made max texture-based corner size a constant Added API for drawing n-gons Added support in circle API to auto-calculate suitable segment count
This commit is contained in:
parent
a0ef8c9187
commit
8e97a4a724
@ -1070,7 +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.).
|
||||
TexturedRoundCorners = true; // Enable using textures instead of strokes to draw rounded corners/circles where possible.
|
||||
TexturedRoundCorners = 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.
|
||||
|
||||
@ -5566,11 +5566,11 @@ static bool AddResizeGrip(ImDrawList* dl, const ImVec2& corner, unsigned int rad
|
||||
if (dl->_Data->Font->ContainerAtlas->Flags & ImFontAtlasFlags_NoBakedRoundCorners) // No data in font
|
||||
return false;
|
||||
|
||||
if (rad < 1 || rad > (unsigned int)dl->_Data->Font->ContainerAtlas->RoundCornersMaxSize) // Radius 0 will cause issues with the UV lookup below
|
||||
if (rad < 1 || rad > ImFontAtlasRoundCornersMaxSize) // Radius 0 will cause issues with the UV lookup below
|
||||
return false;
|
||||
|
||||
// Calculate UVs for the three points we are interested in from the texture
|
||||
const ImVec4& uvs = (*dl->_Data->TexUvRoundCornerFilled)[rad - 1];
|
||||
const ImVec4& uvs = (*dl->_Data->TexRoundCornerData)[rad - 1].TexUvFilled;
|
||||
// uv[0] is the mid-point from the corner towards the centre 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)
|
||||
@ -6947,8 +6947,7 @@ void ImGui::SetCurrentFont(ImFont* font)
|
||||
ImFontAtlas* atlas = g.Font->ContainerAtlas;
|
||||
g.DrawListSharedData.TexUvWhitePixel = atlas->TexUvWhitePixel;
|
||||
g.DrawListSharedData.TexUvLines = atlas->TexUvLines;
|
||||
g.DrawListSharedData.TexUvRoundCornerFilled = &atlas->TexUvRoundCornerFilled;
|
||||
g.DrawListSharedData.TexUvRoundCornerStroked = &atlas->TexUvRoundCornerStroked;
|
||||
g.DrawListSharedData.TexRoundCornerData = &atlas->TexRoundCornerData;
|
||||
g.DrawListSharedData.Font = g.Font;
|
||||
g.DrawListSharedData.FontSize = g.FontSize;
|
||||
}
|
||||
|
16
imgui.h
16
imgui.h
@ -2701,6 +2701,15 @@ 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
|
||||
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
|
||||
};
|
||||
|
||||
// See ImFontAtlas::AddCustomRectXXX functions.
|
||||
struct ImFontAtlasCustomRect
|
||||
{
|
||||
@ -2835,12 +2844,7 @@ 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
|
||||
|
||||
// FIXME-ROUNDCORNERS: WIP
|
||||
// FIXME: avoid so many allocations, statically sized buffer removing an indirection may be beneficial here?
|
||||
int RoundCornersMaxSize; // Max pixel size of round corner textures to generate
|
||||
ImVector<int> RoundCornersRectIds; // Ids of custom rects for round corners indexed by size [0] is 1px, [n] is (n+1)px (index up to RoundCornersMaxSize - 1).
|
||||
ImVector<ImVec4> TexUvRoundCornerFilled; // Texture coordinates to filled round corner quads
|
||||
ImVector<ImVec4> TexUvRoundCornerStroked;// Texture coordinates to stroked round corner quads
|
||||
ImVector<ImFontRoundedCornerData> TexRoundCornerData; // Data for texture-based round corners indexed by size [0] is 1px, [n] is (n+1)px (index up to ImFontAtlasRoundCornersMaxSize - 1).
|
||||
|
||||
// [Obsolete]
|
||||
//typedef ImFontAtlasCustomRect CustomRect; // OBSOLETED in 1.72+
|
||||
|
@ -258,12 +258,7 @@ static void GetVtxIdxDelta(ImDrawList* dl, int* vtx, int *idx)
|
||||
}
|
||||
|
||||
// https://github.com/ocornut/imgui/issues/1962
|
||||
// FIXME-ROUNDCORNERS: Sizes aren't matching.
|
||||
// FIXME-ROUNDCORNERS: Lift the RoundCornersMaxSize limitation, fallback on existing renderer.
|
||||
// FIXME-ROUNDCORNERS: Work on reducing filtrate for stroked shapes (may need to trade some cpu/vtx to reduce fill-rate for e.g. simple stroked circle) > https://github.com/ocornut/imgui/issues/1962#issuecomment-411507917
|
||||
// FIXME-ROUNDCORNERS: Figure out how to support multiple thickness, might hard-code common steps (1.0, 1.5, 2.0, 3.0), not super satisfactory but may be best
|
||||
// FIXME-ROUNDCORNERS: AddCircle* API relying on num_segments may need rework, might obsolete this parameter, or make it 0 default and rely on automatic subdivision similar to style.CurveTessellationTol for Bezier
|
||||
// FIXME-ROUNDCORNERS: Intentional "low segment count" shapes (e.g. hexagon) currently achieved with AddCircle may need a new API (AddNgon?)
|
||||
static void TestTextureBasedRender()
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
@ -273,10 +268,11 @@ static void TestTextureBasedRender()
|
||||
|
||||
ImGui::Text("Hold SHIFT to toggle (%s)", io.KeyShift ? "SHIFT ON -- Using textures." : "SHIFT OFF -- Old method.");
|
||||
|
||||
static float radius = io.Fonts->RoundCornersMaxSize * 0.5f;
|
||||
static float radius = 16.0f; // ImFontAtlasRoundCornersMaxSize * 0.5f;
|
||||
static int segments = 20;
|
||||
static int ngon_segments = 6;
|
||||
|
||||
ImGui::SliderFloat("radius", &radius, 0.0f, (float)io.Fonts->RoundCornersMaxSize, "%.0f");
|
||||
ImGui::SliderFloat("radius", &radius, 0.0f, (float)64 /*ImFontAtlasRoundCornersMaxSize*/, "%.0f");
|
||||
|
||||
int vtx_n = 0;
|
||||
int idx_n = 0;
|
||||
@ -286,7 +282,7 @@ static void TestTextureBasedRender()
|
||||
ImGui::BeginGroup();
|
||||
|
||||
ImGui::PushItemWidth(120);
|
||||
ImGui::SliderInt("segments", &segments, 3, 100);
|
||||
ImGui::SliderInt("segments", &segments, 0, 100);
|
||||
ImGui::PopItemWidth();
|
||||
|
||||
{
|
||||
@ -347,6 +343,37 @@ static void TestTextureBasedRender()
|
||||
|
||||
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);
|
||||
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");
|
||||
|
162
imgui_draw.cpp
162
imgui_draw.cpp
@ -1412,7 +1412,7 @@ inline bool AddRoundCornerRect(ImDrawList* draw_list, const ImVec2& a, const ImV
|
||||
return false;
|
||||
|
||||
if ((rad <= 0) || // Zero radius causes issues with the [rad - 1] UV lookup below
|
||||
(rad > data->Font->ContainerAtlas->RoundCornersMaxSize))
|
||||
(rad > ImFontAtlasRoundCornersMaxSize))
|
||||
{
|
||||
// We can't handle this
|
||||
return false;
|
||||
@ -1425,6 +1425,13 @@ inline bool AddRoundCornerRect(ImDrawList* draw_list, const ImVec2& a, const ImV
|
||||
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.
|
||||
|
||||
// The width of our stroke for unfilled mode
|
||||
// Something of a placeholder at the moment - used for calculations but without appropriately-generated
|
||||
// textures won't actually achieve anything
|
||||
const float stroke_width = 1.0f;
|
||||
|
||||
ImFontRoundedCornerData& round_corner_data = (*data->TexRoundCornerData)[rad - 1];
|
||||
|
||||
// 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)
|
||||
@ -1432,7 +1439,7 @@ inline bool AddRoundCornerRect(ImDrawList* draw_list, const ImVec2& a, const ImV
|
||||
// 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
|
||||
const ImVec4& uvs = (*(fill ? data->TexUvRoundCornerFilled : data->TexUvRoundCornerStroked))[rad - 1];
|
||||
const ImVec4& uvs = fill ? round_corner_data.TexUvFilled : round_corner_data.TexUvStroked;
|
||||
const ImVec2 corner_uv[3] =
|
||||
{
|
||||
ImVec2(uvs.x, uvs.y),
|
||||
@ -1465,19 +1472,26 @@ inline bool AddRoundCornerRect(ImDrawList* draw_list, const ImVec2& a, const ImV
|
||||
// MDX ID--------IC MCX
|
||||
// | |
|
||||
// CD--MDY--------MCY--CC
|
||||
//
|
||||
// MAX2/MAY2/etc are those vertices offset inwards by the line width
|
||||
// (only used for unfilled rectangles)
|
||||
|
||||
const ImVec2 ca(a.x, a.y), cb(b.x, a.y);
|
||||
const ImVec2 may(ca.x + rad, ca.y), mby(cb.x - rad, cb.y);
|
||||
const ImVec2 may2(may.x, may.y + stroke_width), mby2(mby.x, mby.y + stroke_width);
|
||||
const ImVec2 max(ca.x, ca.y + rad), mbx(cb.x, cb.y + rad);
|
||||
const ImVec2 max2(max.x + stroke_width, max.y), mbx2(mbx.x - stroke_width, mbx.y);
|
||||
const ImVec2 ia(ca.x + rad, ca.y + rad), ib(cb.x - rad, cb.y + rad);
|
||||
|
||||
const ImVec2 cc(b.x, b.y), cd(a.x, b.y);
|
||||
const ImVec2 mdx(cd.x, cd.y - rad), mcx(cc.x, cc.y - rad);
|
||||
const ImVec2 mdx2(mdx.x + stroke_width, mdx.y), mcx2(mcx.x - stroke_width, mcx.y);
|
||||
const ImVec2 mdy(cd.x + rad, cd.y), mcy(cc.x - rad, cc.y);
|
||||
const ImVec2 mdy2(mdy.x, mdy.y - stroke_width), mcy2(mcy.x, mcy.y - stroke_width);
|
||||
const ImVec2 id(cd.x + rad, cd.y - rad), ic(cc.x - rad, cc.y - rad);
|
||||
|
||||
// Reserve enough space for the vertices/indices
|
||||
const int vtcs = 16;
|
||||
const int vtcs = fill ? 16 : 24;
|
||||
const int idcs = fill ? (18 * 3) : (16 * 3);
|
||||
draw_list->PrimReserve(idcs, vtcs);
|
||||
|
||||
@ -1490,6 +1504,35 @@ inline bool AddRoundCornerRect(ImDrawList* draw_list, const ImVec2& a, const ImV
|
||||
draw_list->_VtxWritePtr[d].uv = corner_uv[(i)]; \
|
||||
draw_list->_VtxWritePtr[d].col = col
|
||||
|
||||
// Write a vertex using an interpolated position and UVs, where
|
||||
// px and py are the parametric position within the corner
|
||||
// (0,0 at the inside, 1,1 at the outside).
|
||||
// "inside" here corresponds to ia/ib/ic/id, whilst "outside" is ca/cb/cc/cd
|
||||
// Corner gives the corner (a/b/c/d) to use
|
||||
// d is the vertex index to write to
|
||||
// The px<py check is necessary because we need to mirror the texture across
|
||||
// the diagonal (as we only have 45 degrees' worth of actual valid pixel data)
|
||||
// This needs to be done the opposite way around for filled vs unfilled as they
|
||||
// each occupy one side of the texture
|
||||
#define VTX_WRITE_LERPED(d, corner, px, py) \
|
||||
draw_list->_VtxWritePtr[d].pos = ImVec2(ImLerp(i##corner.x, c##corner.x, px), ImLerp(i##corner.y, c##corner.y, py)); \
|
||||
draw_list->_VtxWritePtr[d].uv = ((px < py) ^ fill) ? \
|
||||
ImVec2(ImLerp(corner_uv[0].x, corner_uv[b##corner ? 2 : 1].x, py), ImLerp(corner_uv[0].y, corner_uv[b##corner ? 2 : 1].y, px)) : \
|
||||
ImVec2(ImLerp(corner_uv[0].x, corner_uv[b##corner ? 2 : 1].x, px), ImLerp(corner_uv[0].y, corner_uv[b##corner ? 2 : 1].y, py)); \
|
||||
draw_list->_VtxWritePtr[d].col = col
|
||||
|
||||
// Optimised versions of the above, for the cases where either px or py is always zero
|
||||
|
||||
#define VTX_WRITE_LERPED_X(d, corner, px) \
|
||||
draw_list->_VtxWritePtr[d].pos = ImVec2(ImLerp(i##corner.x, c##corner.x, px), i##corner.y); \
|
||||
draw_list->_VtxWritePtr[d].uv = ImVec2(ImLerp(corner_uv[0].x, corner_uv[b##corner ? 2 : 1].x, px), corner_uv[0].y); \
|
||||
draw_list->_VtxWritePtr[d].col = col
|
||||
|
||||
#define VTX_WRITE_LERPED_Y(d, corner, py) \
|
||||
draw_list->_VtxWritePtr[d].pos = ImVec2(i##corner.x, ImLerp(i##corner.y, c##corner.y, py)); \
|
||||
draw_list->_VtxWritePtr[d].uv = ImVec2(ImLerp(corner_uv[0].x, corner_uv[b##corner ? 2 : 1].x, py), corner_uv[0].y); \
|
||||
draw_list->_VtxWritePtr[d].col = col
|
||||
|
||||
// Set up the outer corners (vca-vcd being the four outermost corners)
|
||||
// If the corner is rounded we use the "empty" corner UV, if not we use the "filled" one.
|
||||
const int vca = 0, vcb = 1, vcc = 2, vcd = 3;
|
||||
@ -1547,6 +1590,39 @@ inline bool AddRoundCornerRect(ImDrawList* draw_list, const ImVec2& a, const ImV
|
||||
VTX_WRITE(vid, id, 0);
|
||||
dv += 12;
|
||||
|
||||
// The unfilled version needs these vertices for the edges
|
||||
|
||||
int vya2 = vca, vxa2 = vca;
|
||||
int vyb2 = vcb, vxb2 = vcb;
|
||||
int vyc2 = vcc, vxc2 = vcc;
|
||||
int vyd2 = vcd, vxd2 = vcd;
|
||||
|
||||
if (!fill)
|
||||
{
|
||||
vya2 = dv;
|
||||
vxa2 = dv + 1;
|
||||
vyb2 = dv + 2;
|
||||
vxb2 = dv + 3;
|
||||
vyc2 = dv + 4;
|
||||
vxc2 = dv + 5;
|
||||
vyd2 = dv + 6;
|
||||
vxd2 = dv + 7;
|
||||
|
||||
const float width_offset_parametric = round_corner_data.ParametricStrokeWidth; // Edge width in our parametric coordinate space
|
||||
const float parametric_offset = 1.0f - width_offset_parametric; // Offset from the centre-most edge
|
||||
|
||||
VTX_WRITE_LERPED_X(vxa2, a, parametric_offset);
|
||||
VTX_WRITE_LERPED_Y(vya2, a, parametric_offset);
|
||||
VTX_WRITE_LERPED_X(vxb2, b, parametric_offset);
|
||||
VTX_WRITE_LERPED_Y(vyb2, b, parametric_offset);
|
||||
VTX_WRITE_LERPED_X(vxc2, c, parametric_offset);
|
||||
VTX_WRITE_LERPED_Y(vyc2, c, parametric_offset);
|
||||
VTX_WRITE_LERPED_X(vxd2, d, parametric_offset);
|
||||
VTX_WRITE_LERPED_Y(vyd2, d, parametric_offset);
|
||||
|
||||
dv += 8;
|
||||
}
|
||||
|
||||
// Here we emit the actual triangles
|
||||
|
||||
int di = 0; // The number of indices we have written
|
||||
@ -1607,20 +1683,20 @@ inline bool AddRoundCornerRect(ImDrawList* draw_list, const ImVec2& a, const ImV
|
||||
// Unfilled version
|
||||
|
||||
// Top edge
|
||||
IDX_WRITE_TRI(vya, via, vib);
|
||||
IDX_WRITE_TRI(vya, vyb, vib);
|
||||
IDX_WRITE_TRI(vya, vya2, vyb2);
|
||||
IDX_WRITE_TRI(vya, vyb, vyb2);
|
||||
|
||||
// Bottom edge
|
||||
IDX_WRITE_TRI(vyd, vid, vic);
|
||||
IDX_WRITE_TRI(vyd, vyc, vic);
|
||||
IDX_WRITE_TRI(vyd, vyd2, vyc2);
|
||||
IDX_WRITE_TRI(vyd, vyc, vyc2);
|
||||
|
||||
// Left edge
|
||||
IDX_WRITE_TRI(vxa, via, vid);
|
||||
IDX_WRITE_TRI(vxa, vxd, vid);
|
||||
IDX_WRITE_TRI(vxa, vxa2, vxd2);
|
||||
IDX_WRITE_TRI(vxa, vxd, vxd2);
|
||||
|
||||
// Right edge
|
||||
IDX_WRITE_TRI(vxb, vib, vic);
|
||||
IDX_WRITE_TRI(vxb, vxc, vic);
|
||||
IDX_WRITE_TRI(vxb, vxb2, vxc2);
|
||||
IDX_WRITE_TRI(vxb, vxc, vxc2);
|
||||
|
||||
// Corners
|
||||
|
||||
@ -1654,6 +1730,9 @@ inline bool AddRoundCornerRect(ImDrawList* draw_list, const ImVec2& a, const ImV
|
||||
|
||||
#undef IDX_WRITE_TRI
|
||||
#undef VTX_WRITE
|
||||
#undef VTX_WRITE_LERPED
|
||||
#undef VTX_WRITE_LERPED_X
|
||||
#undef VTX_WRITE_LERPED_Y
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -1788,13 +1867,15 @@ inline bool AddRoundCornerCircle(ImDrawList* draw_list, const ImVec2& center, fl
|
||||
return false;
|
||||
|
||||
const int rad = (int)radius;
|
||||
if (rad < 1 || rad > data->Font->ContainerAtlas->RoundCornersMaxSize) // Radius 0 will cause issues with the UV lookup below
|
||||
if (rad < 1 || rad > ImFontAtlasRoundCornersMaxSize) // Radius 0 will cause issues with the UV lookup below
|
||||
return false; // We can't handle this
|
||||
|
||||
// Debug command to force this render path to only execute when shift is held
|
||||
if (!ImGui::GetIO().KeyShift)
|
||||
return false;
|
||||
|
||||
ImFontRoundedCornerData& round_corner_data = (*data->TexRoundCornerData)[rad - 1];
|
||||
|
||||
// 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)
|
||||
@ -1802,7 +1883,7 @@ inline bool AddRoundCornerCircle(ImDrawList* draw_list, const ImVec2& center, fl
|
||||
// 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
|
||||
const ImVec4& uvs = (*(fill ? data->TexUvRoundCornerFilled : data->TexUvRoundCornerStroked))[rad - 1];
|
||||
const ImVec4& uvs = fill ? round_corner_data.TexUvFilled : round_corner_data.TexUvStroked;
|
||||
const ImVec2 corner_uv[3] =
|
||||
{
|
||||
ImVec2(uvs.x, uvs.y),
|
||||
@ -1810,8 +1891,8 @@ inline bool AddRoundCornerCircle(ImDrawList* draw_list, const ImVec2& center, fl
|
||||
ImVec2(uvs.z, uvs.w),
|
||||
};
|
||||
|
||||
// Our line width (requires a texture with the appropriate line width to actually do anything)
|
||||
const float line_width = 1.0f;
|
||||
// Our stroke width (requires a texture with the appropriate stroke width to actually do anything)
|
||||
const float stroke_width = 1.0f;
|
||||
|
||||
// Calculate the circle bounds
|
||||
const ImVec2& c = center;
|
||||
@ -1820,7 +1901,7 @@ inline bool AddRoundCornerCircle(ImDrawList* draw_list, const ImVec2& center, fl
|
||||
|
||||
// Some useful constants for our calculations
|
||||
const float half_sqrt_two = 0.70710678f; // sqrtf(2.0f) * 0.5f
|
||||
const float width_offset_parametric = line_width / rad; // Line width in our parametric coordinate space
|
||||
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
|
||||
@ -1866,13 +1947,13 @@ inline bool AddRoundCornerCircle(ImDrawList* draw_list, const ImVec2& center, fl
|
||||
ImVec2 uvi_cardinal = ImVec2(ImLerp(corner_uv[0].x, corner_uv[2].x, 1.0f - width_offset_parametric), corner_uv[0].y);
|
||||
|
||||
// Inner vertices, starting from the left
|
||||
VTX_WRITE(8, ImVec2(tl.x + line_width, c.y), uvi_cardinal);
|
||||
VTX_WRITE(8, ImVec2(tl.x + stroke_width, c.y), uvi_cardinal);
|
||||
VTX_WRITE(9, tlbi, uvbi);
|
||||
VTX_WRITE(10, ImVec2(c.x, tl.y + line_width), uvi_cardinal);
|
||||
VTX_WRITE(10, ImVec2(c.x, tl.y + stroke_width), uvi_cardinal);
|
||||
VTX_WRITE(11, trbi, uvbi);
|
||||
VTX_WRITE(12, ImVec2(br.x - line_width, c.y), uvi_cardinal);
|
||||
VTX_WRITE(12, ImVec2(br.x - stroke_width, c.y), uvi_cardinal);
|
||||
VTX_WRITE(13, brbi, uvbi);
|
||||
VTX_WRITE(14, ImVec2(c.x, br.y - line_width), uvi_cardinal);
|
||||
VTX_WRITE(14, ImVec2(c.x, br.y - stroke_width), uvi_cardinal);
|
||||
VTX_WRITE(15, blbi, uvbi);
|
||||
}
|
||||
|
||||
@ -1967,6 +2048,7 @@ void ImDrawList::AddCircle(const ImVec2& center, float radius, ImU32 col, int nu
|
||||
void ImDrawList::AddCircleFilled(const ImVec2& center, float radius, ImU32 col, int num_segments)
|
||||
{
|
||||
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, col, true))
|
||||
@ -1994,7 +2076,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
|
||||
@ -2441,8 +2523,6 @@ ImFontAtlas::ImFontAtlas()
|
||||
memset(this, 0, sizeof(*this));
|
||||
TexGlyphPadding = 1;
|
||||
PackIdMouseCursors = PackIdLines = -1;
|
||||
|
||||
RoundCornersMaxSize = 32;
|
||||
}
|
||||
|
||||
ImFontAtlas::~ImFontAtlas()
|
||||
@ -2471,7 +2551,7 @@ void ImFontAtlas::ClearInputData()
|
||||
ConfigData.clear();
|
||||
CustomRects.clear();
|
||||
PackIdMouseCursors = PackIdLines = -1;
|
||||
RoundCornersRectIds.clear();
|
||||
TexRoundCornerData.clear();
|
||||
// Important: we leave TexReady untouched
|
||||
}
|
||||
|
||||
@ -2485,8 +2565,6 @@ void ImFontAtlas::ClearTexData()
|
||||
TexPixelsAlpha8 = NULL;
|
||||
TexPixelsRGBA32 = NULL;
|
||||
TexPixelsUseColors = false;
|
||||
TexUvRoundCornerFilled.clear();
|
||||
TexUvRoundCornerStroked.clear();
|
||||
// Important: we leave TexReady untouched
|
||||
}
|
||||
|
||||
@ -3257,18 +3335,23 @@ const int FONT_ATLAS_ROUNDED_CORNER_TEX_CENTER_PADDING = 4;
|
||||
// Register the rectangles we need for the rounded corner images
|
||||
static void ImFontAtlasBuildRegisterRoundCornersCustomRects(ImFontAtlas* atlas)
|
||||
{
|
||||
if (atlas->RoundCornersRectIds.Size > 0)
|
||||
if (atlas->TexRoundCornerData.Size > 0)
|
||||
return;
|
||||
if ((atlas->Flags & ImFontAtlasFlags_NoBakedRoundCorners))
|
||||
return;
|
||||
|
||||
const int pad = FONT_ATLAS_ROUNDED_CORNER_TEX_PADDING;
|
||||
const int max = atlas->RoundCornersMaxSize;
|
||||
for (int n = 0; n < max; n++)
|
||||
const unsigned int max = ImFontAtlasRoundCornersMaxSize;
|
||||
|
||||
atlas->TexRoundCornerData.reserve(max);
|
||||
|
||||
for (unsigned int n = 0; n < max; n++)
|
||||
{
|
||||
const int width = n + 1 + pad * 2;
|
||||
const int height = n + 1 + FONT_ATLAS_ROUNDED_CORNER_TEX_CENTER_PADDING + pad * 2;
|
||||
atlas->RoundCornersRectIds.push_back(atlas->AddCustomRectRegular(width, height));
|
||||
ImFontRoundedCornerData corner_data;
|
||||
corner_data.RectId = atlas->AddCustomRectRegular(width, height);
|
||||
atlas->TexRoundCornerData.push_back(corner_data);
|
||||
}
|
||||
}
|
||||
|
||||
@ -3276,20 +3359,19 @@ static void ImFontAtlasBuildRegisterRoundCornersCustomRects(ImFontAtlas* atlas)
|
||||
static void ImFontAtlasBuildRenderRoundCornersTexData(ImFontAtlas* atlas)
|
||||
{
|
||||
IM_ASSERT(atlas->TexPixelsAlpha8 != NULL);
|
||||
IM_ASSERT(atlas->TexUvRoundCornerFilled.Size == 0);
|
||||
IM_ASSERT(atlas->TexUvRoundCornerStroked.Size == 0);
|
||||
if (atlas->Flags & ImFontAtlasFlags_NoBakedRoundCorners)
|
||||
return;
|
||||
|
||||
// Render the texture
|
||||
const int w = atlas->TexWidth;
|
||||
const unsigned int max = atlas->RoundCornersMaxSize;
|
||||
const unsigned int max = ImFontAtlasRoundCornersMaxSize;
|
||||
const int pad = FONT_ATLAS_ROUNDED_CORNER_TEX_PADDING;
|
||||
IM_ASSERT(atlas->TexRoundCornerData.Size == (int)max); // ImFontAtlasBuildRegisterRoundCornersCustomRects() will have created this for us
|
||||
for (unsigned int n = 0; n < max; n++)
|
||||
{
|
||||
const unsigned int id = n;
|
||||
IM_ASSERT((int)n < atlas->RoundCornersRectIds.Size);
|
||||
ImFontAtlasCustomRect& r = atlas->CustomRects[atlas->RoundCornersRectIds[id]];
|
||||
ImFontRoundedCornerData& data = atlas->TexRoundCornerData[id];
|
||||
ImFontAtlasCustomRect& r = atlas->CustomRects[data.RectId];
|
||||
IM_ASSERT(r.IsPacked());
|
||||
IM_ASSERT(r.Width == n + 1 + pad * 2 && r.Height == n + 1 + FONT_ATLAS_ROUNDED_CORNER_TEX_CENTER_PADDING + pad * 2);
|
||||
|
||||
@ -3303,6 +3385,9 @@ static void ImFontAtlasBuildRenderRoundCornersTexData(ImFontAtlas* atlas)
|
||||
const int radius = (int)(r.Width - pad * 2);
|
||||
const float stroke_width = 1.0f;
|
||||
|
||||
// Pre-calcuate the parameteric stroke width
|
||||
data.ParametricStrokeWidth = stroke_width / (float)radius;
|
||||
|
||||
for (int y = -pad; y < (int)(radius + FONT_ATLAS_ROUNDED_CORNER_TEX_CENTER_PADDING); y++)
|
||||
for (int x = -pad; x < (int)(radius); x++)
|
||||
{
|
||||
@ -3351,8 +3436,11 @@ static void ImFontAtlasBuildRenderRoundCornersTexData(ImFontAtlas* atlas)
|
||||
|
||||
ImVec2 uv0, uv1;
|
||||
atlas->CalcCustomRectUV(&stage_rect, &uv0, &uv1);
|
||||
ImVector<ImVec4>& uvs = (filled ? atlas->TexUvRoundCornerFilled : atlas->TexUvRoundCornerStroked);
|
||||
uvs.push_back(ImVec4(uv0.x, uv0.y, uv1.x, uv1.y));
|
||||
|
||||
if (stage == 0)
|
||||
data.TexUvFilled = ImVec4(uv0.x, uv0.y, uv1.x, uv1.y);
|
||||
else
|
||||
data.TexUvStroked = ImVec4(uv0.x, uv0.y, uv1.x, uv1.y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -726,8 +726,7 @@ struct IMGUI_API ImDrawListSharedData
|
||||
const ImVec4* TexUvLines; // UV of anti-aliased lines in the atlas
|
||||
|
||||
// FIXME-ROUNDCORNERS: WIP + need to remove CircleVtx12 before PR
|
||||
ImVector<ImVec4>* TexUvRoundCornerFilled; // UV of filled round corner quad in the atlas
|
||||
ImVector<ImVec4>* TexUvRoundCornerStroked; // UV of stroked round corner quad in the atlas
|
||||
ImVector<ImFontRoundedCornerData>* TexRoundCornerData; // Data for texture-based rounded corners, indexed by radius
|
||||
|
||||
ImDrawListSharedData();
|
||||
void SetCircleTessellationMaxError(float max_error);
|
||||
@ -2876,7 +2875,6 @@ namespace ImGui
|
||||
|
||||
} // namespace ImGui
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// [SECTION] ImFontAtlas internal API
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -2891,6 +2889,7 @@ struct ImFontBuilderIO
|
||||
#ifdef IMGUI_ENABLE_STB_TRUETYPE
|
||||
IMGUI_API const ImFontBuilderIO* ImFontAtlasGetBuilderForStbTruetype();
|
||||
#endif
|
||||
const unsigned int ImFontAtlasRoundCornersMaxSize = 32; // Maximum size of rounded corner texture to generate in fonts
|
||||
IMGUI_API void ImFontAtlasBuildInit(ImFontAtlas* atlas);
|
||||
IMGUI_API void ImFontAtlasBuildSetupFont(ImFontAtlas* atlas, ImFont* font, ImFontConfig* font_config, float ascent, float descent);
|
||||
IMGUI_API void ImFontAtlasBuildPackCustomRects(ImFontAtlas* atlas, void* stbrp_context_opaque);
|
||||
|
Loading…
Reference in New Issue
Block a user