mirror of
https://github.com/Drezil/imgui.git
synced 2024-11-15 01:17:00 +00:00
Texture-based round corners: Use separate textures for square corners and fix a bunch of corner-case issues
This commit is contained in:
parent
5820a7e248
commit
75f5aaaa1c
@ -6852,6 +6852,7 @@ void ImGui::SetCurrentFont(ImFont* font)
|
|||||||
g.DrawListSharedData.TexUvWhitePixel = atlas->TexUvWhitePixel;
|
g.DrawListSharedData.TexUvWhitePixel = atlas->TexUvWhitePixel;
|
||||||
g.DrawListSharedData.TexUvLines = atlas->TexUvLines;
|
g.DrawListSharedData.TexUvLines = atlas->TexUvLines;
|
||||||
g.DrawListSharedData.TexRoundCornerData = &atlas->TexRoundCornerData;
|
g.DrawListSharedData.TexRoundCornerData = &atlas->TexRoundCornerData;
|
||||||
|
g.DrawListSharedData.TexSquareCornerData = &atlas->TexSquareCornerData;
|
||||||
g.DrawListSharedData.Font = g.Font;
|
g.DrawListSharedData.Font = g.Font;
|
||||||
g.DrawListSharedData.FontSize = g.FontSize;
|
g.DrawListSharedData.FontSize = g.FontSize;
|
||||||
}
|
}
|
||||||
|
1
imgui.h
1
imgui.h
@ -2846,6 +2846,7 @@ struct ImFontAtlas
|
|||||||
int PackIdLines; // Custom texture rectangle ID for baked anti-aliased lines
|
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> 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]
|
// [Obsolete]
|
||||||
//typedef ImFontAtlasCustomRect CustomRect; // OBSOLETED in 1.72+
|
//typedef ImFontAtlasCustomRect CustomRect; // OBSOLETED in 1.72+
|
||||||
|
@ -265,8 +265,16 @@ static void TestTextureBasedRender()
|
|||||||
|
|
||||||
ImGui::Begin("tex_round_corners");
|
ImGui::Begin("tex_round_corners");
|
||||||
|
|
||||||
style.RoundCornersUseTex = io.KeyShift;
|
bool old_round_corners_use_tex = style.RoundCornersUseTex;
|
||||||
ImGui::Checkbox("style.RoundCornersUseTex (hold SHIFT to toggle)", &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 float radius = 16.0f; // ImFontAtlasRoundCornersMaxSize * 0.5f;
|
||||||
static int segments = 20;
|
static int segments = 20;
|
||||||
@ -274,6 +282,11 @@ static void TestTextureBasedRender()
|
|||||||
|
|
||||||
ImGui::SliderFloat("radius", &radius, 0.0f, 64.0f /*(float)ImFontAtlasRoundCornersMaxSize*/, "%.0f");
|
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;
|
static float stroke_width = 1.0f;
|
||||||
|
|
||||||
ImGui::SliderFloat("stroke_width", &stroke_width, 1.0f, 10.0f, "%.0f");
|
ImGui::SliderFloat("stroke_width", &stroke_width, 1.0f, 10.0f, "%.0f");
|
||||||
@ -326,8 +339,9 @@ static void TestTextureBasedRender()
|
|||||||
|
|
||||||
{
|
{
|
||||||
ImGui::Button("##3", ImVec2(200, 200));
|
ImGui::Button("##3", ImVec2(200, 200));
|
||||||
ImVec2 r_min = ImGui::GetItemRectMin();
|
ImVec2 size = ImGui::GetItemRectSize();
|
||||||
ImVec2 r_max = ImGui::GetItemRectMax();
|
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);
|
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);
|
draw_list->AddRectFilled(r_min, r_max, IM_COL32(255,0,255,255), radius, corner_flags ? corner_flags : ImDrawFlags_RoundCornersNone);
|
||||||
@ -336,8 +350,10 @@ static void TestTextureBasedRender()
|
|||||||
}
|
}
|
||||||
{
|
{
|
||||||
ImGui::Button("##4", ImVec2(200, 200));
|
ImGui::Button("##4", ImVec2(200, 200));
|
||||||
ImVec2 r_min = ImGui::GetItemRectMin();
|
ImVec2 size = ImGui::GetItemRectSize();
|
||||||
ImVec2 r_max = ImGui::GetItemRectMax();
|
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);
|
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);
|
draw_list->AddRect(r_min, r_max, IM_COL32(255,0,255,255), radius, corner_flags, stroke_width);
|
||||||
@ -390,6 +406,8 @@ static void TestTextureBasedRender()
|
|||||||
ImFontAtlas* atlas = ImGui::GetIO().Fonts;
|
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));
|
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();
|
ImGui::End();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
740
imgui_draw.cpp
740
imgui_draw.cpp
@ -1420,10 +1420,27 @@ inline bool AddRoundCornerRect(ImDrawList* draw_list, const ImVec2& a, const ImV
|
|||||||
if (rad <= 0 || rad > ImFontAtlasRoundCornersMaxSize)
|
if (rad <= 0 || rad > ImFontAtlasRoundCornersMaxSize)
|
||||||
return false; // We can't handle this
|
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);
|
const unsigned int index = (stroke_width - 1) + ((rad - 1) * ImFontAtlasRoundCornersMaxStrokeWidth);
|
||||||
ImFontRoundedCornerData& round_corner_data = (*data->TexRoundCornerData)[index];
|
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)
|
if ((round_corner_data.RectId < 0) || (square_corner_data.RectId < 0))
|
||||||
return false; // No data for this configuration
|
return false; // No data for this configuration
|
||||||
|
|
||||||
ImTextureID tex_id = data->Font->ContainerAtlas->TexID;
|
ImTextureID tex_id = data->Font->ContainerAtlas->TexID;
|
||||||
@ -1437,14 +1454,24 @@ inline bool AddRoundCornerRect(ImDrawList* draw_list, const ImVec2& a, const ImV
|
|||||||
// This represents a 45 degree "wedge" of circle, which then gets mirrored here to produce a 90 degree curve
|
// 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
|
// 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"
|
// 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
|
// corner of the area on the texture page, so we need to calculate UVs appropriately
|
||||||
const ImVec4& uvs = fill ? round_corner_data.TexUvFilled : round_corner_data.TexUvStroked;
|
const ImVec4& round_uvs = fill ? round_corner_data.TexUvFilled : round_corner_data.TexUvStroked;
|
||||||
const bool use_alternative_uvs = fill | round_corner_data.StrokedUsesAlternateUVs;
|
const bool round_use_alternative_uvs = fill | round_corner_data.StrokedUsesAlternateUVs;
|
||||||
const ImVec2 corner_uv[3] =
|
const ImVec2 round_corner_uv[3] =
|
||||||
{
|
{
|
||||||
ImVec2(uvs.x, uvs.y),
|
ImVec2(round_uvs.x, round_uvs.y),
|
||||||
use_alternative_uvs ? ImVec2(uvs.x, uvs.w) : ImVec2(uvs.z, uvs.y),
|
round_use_alternative_uvs ? ImVec2(round_uvs.x, round_uvs.w) : ImVec2(round_uvs.z, round_uvs.y),
|
||||||
ImVec2(uvs.z, uvs.w)
|
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:
|
// In this code A-D represent the four corners of the rectangle, going clockwise from the top-left:
|
||||||
@ -1458,6 +1485,22 @@ inline bool AddRoundCornerRect(ImDrawList* draw_list, const ImVec2& a, const ImV
|
|||||||
const bool bc = (flags & ImDrawFlags_RoundCornersBottomRight) != 0;
|
const bool bc = (flags & ImDrawFlags_RoundCornersBottomRight) != 0;
|
||||||
const bool bd = (flags & ImDrawFlags_RoundCornersBottomLeft) != 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
|
// The base vertices for the rectangle
|
||||||
//
|
//
|
||||||
// C are the corner vertices, I the interior ones,
|
// C are the corner vertices, I the interior ones,
|
||||||
@ -1472,247 +1515,206 @@ inline bool AddRoundCornerRect(ImDrawList* draw_list, const ImVec2& a, const ImV
|
|||||||
// MDX ID--------IC MCX
|
// MDX ID--------IC MCX
|
||||||
// | |
|
// | |
|
||||||
// CD--MDY--------MCY--CC
|
// CD--MDY--------MCY--CC
|
||||||
//
|
|
||||||
// MAX2/MAY2/etc are those vertices offset inwards by the line width
|
|
||||||
// (only used for unfilled rectangles)
|
|
||||||
|
|
||||||
// Adjust size to account for the fact that wider strokes draw "outside the box"
|
// Adjust size to account for the fact that wider strokes draw "outside the box"
|
||||||
const float stroke_width_size_expansion = stroke_width - 1.0f;
|
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 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 + rad, ca.y), mby(cb.x - rad, cb.y);
|
const ImVec2 may(ca.x + size_a, ca.y), mby(cb.x - size_b, 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 + size_a), mbx(cb.x, cb.y + size_b);
|
||||||
const ImVec2 max(ca.x, ca.y + rad), mbx(cb.x, cb.y + rad);
|
const ImVec2 ia(ca.x + size_a, ca.y + size_a), ib(cb.x - size_b, cb.y + size_b);
|
||||||
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 + 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 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 - rad), mcx(cc.x, cc.y - rad);
|
const ImVec2 mdx(cd.x, cd.y - size_d), mcx(cc.x, cc.y - size_c);
|
||||||
const ImVec2 mdx2(mdx.x + stroke_width, mdx.y), mcx2(mcx.x - stroke_width, mcx.y);
|
const ImVec2 mdy(cd.x + size_d, cd.y), mcy(cc.x - size_c, cc.y);
|
||||||
const ImVec2 mdy(cd.x + rad, cd.y), mcy(cc.x - rad, cc.y);
|
const ImVec2 id(cd.x + size_d, cd.y - size_d), ic(cc.x - size_c, cc.y - size_c);
|
||||||
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);
|
// 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 + top_side_thickness);
|
||||||
|
const ImVec2 tbl(may.x, may.y + top_side_thickness);
|
||||||
|
|
||||||
|
const ImVec2 btl(mdy.x, mdy.y - bottom_side_thickness);
|
||||||
|
const ImVec2 btr(mcy.x, mcy.y - bottom_side_thickness);
|
||||||
|
const ImVec2 bbr = mcy;
|
||||||
|
const ImVec2 bbl = mdy;
|
||||||
|
|
||||||
|
const ImVec2 ltl = max;
|
||||||
|
const ImVec2 ltr(max.x + left_side_thickness, max.y);
|
||||||
|
const ImVec2 lbr(mdx.x + left_side_thickness, mdx.y);
|
||||||
|
const ImVec2 lbl = mdx;
|
||||||
|
|
||||||
|
const ImVec2 rtl(mbx.x - right_side_thickness, mbx.y);
|
||||||
|
const ImVec2 rtr = mbx;
|
||||||
|
const ImVec2 rbr = mcx;
|
||||||
|
const ImVec2 rbl(mcx.x - right_side_thickness, mcx.y);
|
||||||
|
|
||||||
// Reserve enough space for the vertices/indices
|
// Reserve enough space for the vertices/indices
|
||||||
const int vtcs = fill ? 16 : 24;
|
const int vtcs = fill ? (4 * 9) : (4 * 8);
|
||||||
const int idcs = fill ? (18 * 3) : (16 * 3);
|
const int idcs = fill ? (6 * 9) : (6 * 8);
|
||||||
draw_list->PrimReserve(idcs, vtcs);
|
draw_list->PrimReserve(idcs, vtcs);
|
||||||
|
|
||||||
const ImDrawIdx idx = (ImDrawIdx)draw_list->_VtxCurrentIdx;
|
const ImDrawIdx idx = (ImDrawIdx)draw_list->_VtxCurrentIdx;
|
||||||
|
|
||||||
// Write a vertex to the draw list, with d being the vertex index,
|
// Snap a position to the nearest pixel to ensure correct rasterisation
|
||||||
// p the position and i the index into the UV list
|
#define SNAP_POS(vec) (ImVec2(ImFloor((vec).x + 0.5f), ImFloor((vec).y + 0.5f)))
|
||||||
#define VTX_WRITE(d, p, i) \
|
|
||||||
draw_list->_VtxWritePtr[d].pos = (p); \
|
// Write a corner vertex to the draw list, with d being the vertex index,
|
||||||
draw_list->_VtxWritePtr[d].uv = corner_uv[(i)]; \
|
// 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
|
draw_list->_VtxWritePtr[d].col = col
|
||||||
|
|
||||||
// Write a vertex using an interpolated position and UVs, where
|
// Write a vertex for one of the edge sections, with d being the vertex index,
|
||||||
// px and py are the parametric position within the corner
|
// p the position, e is the edge (t/l/b/r) and i the index into the UV list
|
||||||
// (0,0 at the inside, 1,1 at the outside).
|
#define VTX_WRITE_EDGE(d, p, e, i) \
|
||||||
// "inside" here corresponds to ia/ib/ic/id, whilst "outside" is ca/cb/cc/cd
|
draw_list->_VtxWritePtr[d].pos = SNAP_POS(p); \
|
||||||
// Corner gives the corner (a/b/c/d) to use
|
draw_list->_VtxWritePtr[d].uv = edge_uv_##e[(i)]; \
|
||||||
// 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_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 = use_alternative_uvs ? \
|
|
||||||
ImVec2(corner_uv[0].x, 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), corner_uv[0].y); \
|
|
||||||
draw_list->_VtxWritePtr[d].col = col
|
draw_list->_VtxWritePtr[d].col = col
|
||||||
|
|
||||||
#define VTX_WRITE_LERPED_Y(d, corner, py) \
|
// Write a vertex for the center fill, with d being the vertex index and
|
||||||
draw_list->_VtxWritePtr[d].pos = ImVec2(i##corner.x, ImLerp(i##corner.y, c##corner.y, py)); \
|
// p the position
|
||||||
draw_list->_VtxWritePtr[d].uv = use_alternative_uvs ? \
|
#define VTX_WRITE_FILL(d, p) \
|
||||||
ImVec2(corner_uv[0].x, ImLerp(corner_uv[0].y, corner_uv[b##corner ? 2 : 1].y, py)) : \
|
draw_list->_VtxWritePtr[d].pos = SNAP_POS(p); \
|
||||||
ImVec2(ImLerp(corner_uv[0].x, corner_uv[b##corner ? 2 : 1].x, py), corner_uv[0].y); \
|
draw_list->_VtxWritePtr[d].uv = round_corner_uv[0]; \
|
||||||
draw_list->_VtxWritePtr[d].col = col
|
draw_list->_VtxWritePtr[d].col = col
|
||||||
|
|
||||||
// Set up the outer corners (vca-vcd being the four outermost corners)
|
int dv = 0; // A count of the number of vertices we've written
|
||||||
// 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;
|
|
||||||
VTX_WRITE(vca, ca, ba ? 2 : 1);
|
|
||||||
VTX_WRITE(vcb, cb, bb ? 2 : 1);
|
|
||||||
VTX_WRITE(vcc, cc, bc ? 2 : 1);
|
|
||||||
VTX_WRITE(vcd, cd, bd ? 2 : 1);
|
|
||||||
|
|
||||||
int dv = 4; // A count of the number of vertices we've written
|
|
||||||
|
|
||||||
// Set up all the other vertices
|
|
||||||
// Initially they all point to the outside corners, and then
|
|
||||||
// we move them in as required for each rounded corner
|
|
||||||
// VX contains the vertex on the "X side" (i.e. offset in Y) of each corner
|
|
||||||
// VY contains the vertex on the "Y side" (i.e. offset in X) of each corner
|
|
||||||
// VI contains the interior vertex
|
|
||||||
//
|
|
||||||
// This is the layout if all corners are rounded:
|
|
||||||
//
|
|
||||||
// ----VYA--------VYB----
|
|
||||||
// | |
|
|
||||||
// VXA VIA------VIB VXB
|
|
||||||
// | | | |
|
|
||||||
// | | | |
|
|
||||||
// XVD VID------VIC VXC
|
|
||||||
// | |
|
|
||||||
// ----VYD--------VYC----
|
|
||||||
//
|
|
||||||
// In addition, for un-filled rectangles VXA2/VYA2/etc will contain the corresponding
|
|
||||||
// vertex offset inwards by the line width
|
|
||||||
|
|
||||||
int vya = dv;
|
|
||||||
int vxa = dv + 1;
|
|
||||||
int via = dv + 2;
|
|
||||||
int vyb = dv + 3;
|
|
||||||
int vxb = dv + 4;
|
|
||||||
int vib = dv + 5;
|
|
||||||
int vyc = dv + 6;
|
|
||||||
int vxc = dv + 7;
|
|
||||||
int vic = dv + 8;
|
|
||||||
int vyd = dv + 9;
|
|
||||||
int vxd = dv + 10;
|
|
||||||
int vid = dv + 11;
|
|
||||||
VTX_WRITE(vya, may, 1);
|
|
||||||
VTX_WRITE(vxa, max, 1);
|
|
||||||
VTX_WRITE(via, ia, 0);
|
|
||||||
VTX_WRITE(vyb, mby, 1);
|
|
||||||
VTX_WRITE(vxb, mbx, 1);
|
|
||||||
VTX_WRITE(vib, ib, 0);
|
|
||||||
VTX_WRITE(vyc, mcy, 1);
|
|
||||||
VTX_WRITE(vxc, mcx, 1);
|
|
||||||
VTX_WRITE(vic, ic, 0);
|
|
||||||
VTX_WRITE(vyd, mdy, 1);
|
|
||||||
VTX_WRITE(vxd, mdx, 1);
|
|
||||||
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 center-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
|
int di = 0; // The number of indices we have written
|
||||||
|
|
||||||
// Write a triangle using three indices
|
// Write a triangle using three indices
|
||||||
#define IDX_WRITE_TRI(idx0, idx1, idx2) \
|
#define IDX_WRITE_TRI(idx0, idx1, idx2) \
|
||||||
draw_list->_IdxWritePtr[di] = (ImDrawIdx)(idx+(idx0)); \
|
draw_list->_IdxWritePtr[di] = (ImDrawIdx)(idx+(idx0)); \
|
||||||
draw_list->_IdxWritePtr[di+1] = (ImDrawIdx)(idx+(idx1)); \
|
draw_list->_IdxWritePtr[di+1] = (ImDrawIdx)(idx+(idx1)); \
|
||||||
draw_list->_IdxWritePtr[di+2] = (ImDrawIdx)(idx+(idx2)); \
|
draw_list->_IdxWritePtr[di+2] = (ImDrawIdx)(idx+(idx2)); \
|
||||||
di += 3
|
di += 3
|
||||||
|
|
||||||
// Two triangles to fill the inner portion of the rectangle
|
// 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)
|
if (fill)
|
||||||
{
|
{
|
||||||
// Each corner emits two triangles for the corner itself,
|
VTX_WRITE_FILL(dv + 0, ia);
|
||||||
// and two triangles that form "wings" attached to the corner
|
VTX_WRITE_FILL(dv + 1, ib);
|
||||||
// Each wing forms one half of the edge (with the wing from the
|
VTX_WRITE_FILL(dv + 2, ic);
|
||||||
// adjacent corner forming the other)
|
VTX_WRITE_FILL(dv + 3, id);
|
||||||
|
|
||||||
// Central section
|
IDX_WRITE_TRI(dv + 0, dv + 1, dv + 2);
|
||||||
IDX_WRITE_TRI(via, vic, vib);
|
IDX_WRITE_TRI(dv + 0, dv + 2, dv + 3);
|
||||||
IDX_WRITE_TRI(via, vic, vid);
|
dv += 4;
|
||||||
|
|
||||||
// Top-left corner
|
|
||||||
IDX_WRITE_TRI(vca, vya, via);
|
|
||||||
IDX_WRITE_TRI(vca, vxa, via);
|
|
||||||
|
|
||||||
// Edge "wing"
|
|
||||||
IDX_WRITE_TRI(vib, vya, via);
|
|
||||||
IDX_WRITE_TRI(vid, vxa, via);
|
|
||||||
|
|
||||||
// Top-right corner
|
|
||||||
IDX_WRITE_TRI(vcb, vyb, vib);
|
|
||||||
IDX_WRITE_TRI(vcb, vxb, vib);
|
|
||||||
|
|
||||||
// Edge "wing"
|
|
||||||
IDX_WRITE_TRI(vya, vyb, vib);
|
|
||||||
IDX_WRITE_TRI(vic, vxb, vib);
|
|
||||||
|
|
||||||
// Bottom-right corner
|
|
||||||
IDX_WRITE_TRI(vcc, vyc, vic);
|
|
||||||
IDX_WRITE_TRI(vcc, vxc, vic);
|
|
||||||
|
|
||||||
// Edge "wing"
|
|
||||||
IDX_WRITE_TRI(vxb, vxc, vic);
|
|
||||||
IDX_WRITE_TRI(vyd, vyc, vic);
|
|
||||||
|
|
||||||
// Bottom-left corner
|
|
||||||
IDX_WRITE_TRI(vcd, vyd, vid);
|
|
||||||
IDX_WRITE_TRI(vcd, vxd, vid);
|
|
||||||
|
|
||||||
// Edge "wing"
|
|
||||||
IDX_WRITE_TRI(vic, vyd, vid);
|
|
||||||
IDX_WRITE_TRI(vxa, vxd, vid);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Unfilled version
|
|
||||||
|
|
||||||
// Top edge
|
|
||||||
IDX_WRITE_TRI(vya, vya2, vyb2);
|
|
||||||
IDX_WRITE_TRI(vya, vyb, vyb2);
|
|
||||||
|
|
||||||
// Bottom edge
|
|
||||||
IDX_WRITE_TRI(vyd, vyd2, vyc2);
|
|
||||||
IDX_WRITE_TRI(vyd, vyc, vyc2);
|
|
||||||
|
|
||||||
// Left edge
|
|
||||||
IDX_WRITE_TRI(vxa, vxa2, vxd2);
|
|
||||||
IDX_WRITE_TRI(vxa, vxd, vxd2);
|
|
||||||
|
|
||||||
// Right edge
|
|
||||||
IDX_WRITE_TRI(vxb, vxb2, vxc2);
|
|
||||||
IDX_WRITE_TRI(vxb, vxc, vxc2);
|
|
||||||
|
|
||||||
// Corners
|
|
||||||
|
|
||||||
// Top-left
|
|
||||||
IDX_WRITE_TRI(vca, vya, via);
|
|
||||||
IDX_WRITE_TRI(vca, vxa, via);
|
|
||||||
|
|
||||||
// Top-right
|
|
||||||
IDX_WRITE_TRI(vcb, vyb, vib);
|
|
||||||
IDX_WRITE_TRI(vcb, vxb, vib);
|
|
||||||
|
|
||||||
// Bottom-right
|
|
||||||
IDX_WRITE_TRI(vcc, vyc, vic);
|
|
||||||
IDX_WRITE_TRI(vcc, vxc, vic);
|
|
||||||
|
|
||||||
// Bottom-left
|
|
||||||
IDX_WRITE_TRI(vcd, vyd, vid);
|
|
||||||
IDX_WRITE_TRI(vcd, vxd, vid);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
draw_list->_VtxWritePtr += dv;
|
draw_list->_VtxWritePtr += dv;
|
||||||
@ -1726,11 +1728,11 @@ inline bool AddRoundCornerRect(ImDrawList* draw_list, const ImVec2& a, const ImV
|
|||||||
// (not required ATM as we always generate the right number)
|
// (not required ATM as we always generate the right number)
|
||||||
//draw_list->PrimUnreserve(idcs - di, vtcs - dv);
|
//draw_list->PrimUnreserve(idcs - di, vtcs - dv);
|
||||||
|
|
||||||
|
#undef SNAP_POS
|
||||||
#undef IDX_WRITE_TRI
|
#undef IDX_WRITE_TRI
|
||||||
#undef VTX_WRITE
|
#undef VTX_WRITE_CORNER
|
||||||
#undef VTX_WRITE_LERPED
|
#undef VTX_WRITE_EDGE
|
||||||
#undef VTX_WRITE_LERPED_X
|
#undef VTX_WRITE_FILL
|
||||||
#undef VTX_WRITE_LERPED_Y
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -3356,41 +3358,47 @@ static void ImFontAtlasBuildRegisterRoundCornersCustomRects(ImFontAtlas* atlas)
|
|||||||
const unsigned int max_radius = ImFontAtlasRoundCornersMaxSize;
|
const unsigned int max_radius = ImFontAtlasRoundCornersMaxSize;
|
||||||
const unsigned int max_thickness = ImFontAtlasRoundCornersMaxStrokeWidth;
|
const unsigned int max_thickness = ImFontAtlasRoundCornersMaxStrokeWidth;
|
||||||
|
|
||||||
atlas->TexRoundCornerData.reserve(max_radius * max_thickness);
|
atlas->TexRoundCornerData.reserve(max_radius * max_thickness * 2);
|
||||||
|
|
||||||
for (unsigned int radius_index = 0; radius_index < max_radius; radius_index++)
|
// We do this twice, one for rounded corners and once for square corners
|
||||||
|
for (int square = 0; square < 2; square++) // 1 for square corners
|
||||||
{
|
{
|
||||||
int spare_rect_id = -1; // The last rectangle ID we generated with a spare half
|
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 int width = radius_index + 1 + pad * 2;
|
int spare_rect_id = -1; // The last rectangle ID we generated with a spare half
|
||||||
const int height = radius_index + 1 + FONT_ATLAS_ROUNDED_CORNER_TEX_CENTER_PADDING + pad * 2;
|
for (unsigned int stroke_width_index = 0; stroke_width_index < max_thickness; stroke_width_index++)
|
||||||
|
|
||||||
ImFontRoundedCornerData corner_data;
|
|
||||||
if (ImFontAtlasRoundCornersStrokeWidthMask & (1 << stroke_width_index))
|
|
||||||
{
|
{
|
||||||
if (stroke_width_index == 0 || spare_rect_id < 0)
|
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))
|
||||||
{
|
{
|
||||||
corner_data.RectId = atlas->AddCustomRectRegular(width, height);
|
if (stroke_width_index == 0 || spare_rect_id < 0)
|
||||||
corner_data.StrokedUsesAlternateUVs = false;
|
{
|
||||||
if (stroke_width_index != 0)
|
corner_data.RectId = atlas->AddCustomRectRegular(width, height);
|
||||||
spare_rect_id = corner_data.RectId;
|
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
|
else
|
||||||
{
|
{
|
||||||
// Pack this into the spare half of the previous rect
|
corner_data.RectId = -1; // Set RectId to -1 if we don't want any data
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
IM_ASSERT_PARANOID(atlas->TexRoundCornerData.Size == (int)index);
|
ImVector<ImFontRoundedCornerData>& data_list = square ? atlas->TexSquareCornerData : atlas->TexRoundCornerData;
|
||||||
atlas->TexRoundCornerData.push_back(corner_data);
|
|
||||||
|
IM_ASSERT_PARANOID(data_list.Size == (int)index);
|
||||||
|
data_list.push_back(corner_data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3406,142 +3414,148 @@ static void ImFontAtlasBuildRenderRoundCornersTexData(ImFontAtlas* atlas)
|
|||||||
const int w = atlas->TexWidth;
|
const int w = atlas->TexWidth;
|
||||||
const unsigned int max = ImFontAtlasRoundCornersMaxSize * ImFontAtlasRoundCornersMaxStrokeWidth;
|
const unsigned int max = ImFontAtlasRoundCornersMaxSize * ImFontAtlasRoundCornersMaxStrokeWidth;
|
||||||
const int pad = FONT_ATLAS_ROUNDED_CORNER_TEX_PADDING;
|
const int pad = FONT_ATLAS_ROUNDED_CORNER_TEX_PADDING;
|
||||||
IM_ASSERT(atlas->TexRoundCornerData.Size == (int)max); // ImFontAtlasBuildRegisterRoundCornersCustomRects() will have created this for us
|
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_radius = ImFontAtlasRoundCornersMaxSize;
|
||||||
const unsigned int max_thickness = ImFontAtlasRoundCornersMaxStrokeWidth;
|
const unsigned int max_thickness = ImFontAtlasRoundCornersMaxStrokeWidth;
|
||||||
|
|
||||||
atlas->TexRoundCornerData.reserve(max_radius * max_thickness);
|
for (int square = 0; square < 2; square++) // 1 = Square corners instead of round
|
||||||
|
{
|
||||||
|
ImVector<ImFontRoundedCornerData>& data_list = square ? atlas->TexSquareCornerData : atlas->TexRoundCornerData;
|
||||||
|
|
||||||
for (unsigned int radius_index = 0; radius_index < max_radius; radius_index++)
|
data_list.reserve(max_radius * max_thickness);
|
||||||
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 = atlas->TexRoundCornerData[index];
|
for (unsigned int radius_index = 0; radius_index < max_radius; radius_index++)
|
||||||
if (data.RectId < 0)
|
for (unsigned int stroke_width_index = 0; stroke_width_index < max_thickness; stroke_width_index++)
|
||||||
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
|
const unsigned int index = stroke_width_index + (radius_index * ImFontAtlasRoundCornersMaxStrokeWidth);
|
||||||
// to generate the second half of the pair again when the main loop comes around
|
const unsigned int radius = radius_index + 1;
|
||||||
stroke_width_index++;
|
const float stroke_width = (float)stroke_width_index + 1;
|
||||||
while (stroke_width_index < max_thickness)
|
|
||||||
|
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)
|
||||||
{
|
{
|
||||||
const unsigned int candidate_index = stroke_width_index + (radius_index * ImFontAtlasRoundCornersMaxStrokeWidth);
|
// We use the fact that we know shared pairs will always appear together to both make this check fast and skip trying
|
||||||
ImFontRoundedCornerData* candidate_data = &atlas->TexRoundCornerData[candidate_index];
|
// to generate the second half of the pair again when the main loop comes around
|
||||||
|
|
||||||
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++;
|
stroke_width_index++;
|
||||||
}
|
while (stroke_width_index < max_thickness)
|
||||||
}
|
|
||||||
|
|
||||||
// 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.
|
|
||||||
int cx = ImMax(x, 0);
|
|
||||||
int cy = ImMax(y, 0);
|
|
||||||
|
|
||||||
// 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.
|
const unsigned int candidate_index = stroke_width_index + (radius_index * ImFontAtlasRoundCornersMaxStrokeWidth);
|
||||||
cy = ImMax(y - FONT_ATLAS_ROUNDED_CORNER_TEX_CENTER_PADDING, 0);
|
ImFontRoundedCornerData* candidate_data = &data_list[candidate_index];
|
||||||
}
|
|
||||||
|
|
||||||
const float dist = ImSqrt((float)(cx*cx+cy*cy)) - (float)(radius - (filled ? 0 : (stroke_width * 0.5f) + 0.5f));
|
if (candidate_data->RectId == data.RectId)
|
||||||
float alpha = 0.0f;
|
|
||||||
if (filled)
|
|
||||||
{
|
|
||||||
if (stroke_width_index > 0)
|
|
||||||
{
|
{
|
||||||
if (other_data)
|
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.
|
||||||
|
int cx = ImMax(x, 0);
|
||||||
|
int cy = ImMax(y, 0);
|
||||||
|
|
||||||
|
// 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(y - FONT_ATLAS_ROUNDED_CORNER_TEX_CENTER_PADDING, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
{
|
{
|
||||||
// Using the filled section to hold a second stroke width variant instead of filled if we are at a stroke width > 1
|
if (other_data)
|
||||||
const float other_dist = 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);
|
// Using the filled section to hold a second stroke width variant instead of filled if we are at a stroke width > 1
|
||||||
const float alpha2 = ImClamp(other_dist, 0.0f, 1.0f);
|
const float other_dist = (square ? ImMax(cx, cy) : ImSqrt((float)(cx*cx + cy*cy))) - (float)(radius - ((other_stroke_width * 0.5f) + 0.5f));
|
||||||
alpha = alpha1 - alpha2;
|
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
|
else
|
||||||
{
|
{
|
||||||
alpha = ImClamp(-dist, 0.0f, 1.0f); // Filled version
|
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
|
else
|
||||||
{
|
{
|
||||||
const float alpha1 = ImClamp(dist + stroke_width, 0.0f, 1.0f);
|
data.TexUvStroked = ImVec4(uv0.x, uv0.y, uv1.x, uv1.y);
|
||||||
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.
|
// This is called/shared by both the stb_truetype and the FreeType builder.
|
||||||
|
@ -726,6 +726,7 @@ struct IMGUI_API ImDrawListSharedData
|
|||||||
const ImVec4* TexUvLines; // UV of anti-aliased lines in the atlas
|
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>* TexRoundCornerData; // Data for texture-based rounded corners, indexed by radius
|
||||||
|
ImVector<ImFontRoundedCornerData>* TexSquareCornerData; // Data for texture-based square corners, indexed by radius
|
||||||
|
|
||||||
ImDrawListSharedData();
|
ImDrawListSharedData();
|
||||||
void SetCircleTessellationMaxError(float max_error);
|
void SetCircleTessellationMaxError(float max_error);
|
||||||
|
Loading…
Reference in New Issue
Block a user