mirror of
https://github.com/Drezil/imgui.git
synced 2024-11-21 19:37:01 +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.TexUvLines = atlas->TexUvLines;
|
||||
g.DrawListSharedData.TexRoundCornerData = &atlas->TexRoundCornerData;
|
||||
g.DrawListSharedData.TexSquareCornerData = &atlas->TexSquareCornerData;
|
||||
g.DrawListSharedData.Font = g.Font;
|
||||
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
|
||||
|
||||
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+
|
||||
|
@ -265,8 +265,16 @@ static void TestTextureBasedRender()
|
||||
|
||||
ImGui::Begin("tex_round_corners");
|
||||
|
||||
style.RoundCornersUseTex = io.KeyShift;
|
||||
ImGui::Checkbox("style.RoundCornersUseTex (hold SHIFT to toggle)", &style.RoundCornersUseTex);
|
||||
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;
|
||||
@ -274,6 +282,11 @@ static void TestTextureBasedRender()
|
||||
|
||||
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");
|
||||
@ -326,8 +339,9 @@ static void TestTextureBasedRender()
|
||||
|
||||
{
|
||||
ImGui::Button("##3", ImVec2(200, 200));
|
||||
ImVec2 r_min = ImGui::GetItemRectMin();
|
||||
ImVec2 r_max = ImGui::GetItemRectMax();
|
||||
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);
|
||||
@ -336,8 +350,10 @@ static void TestTextureBasedRender()
|
||||
}
|
||||
{
|
||||
ImGui::Button("##4", ImVec2(200, 200));
|
||||
ImVec2 r_min = ImGui::GetItemRectMin();
|
||||
ImVec2 r_max = ImGui::GetItemRectMax();
|
||||
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);
|
||||
@ -390,6 +406,8 @@ static void TestTextureBasedRender()
|
||||
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();
|
||||
}
|
||||
|
||||
|
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)
|
||||
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)
|
||||
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;
|
||||
@ -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
|
||||
// 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] =
|
||||
// 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(uvs.x, uvs.y),
|
||||
use_alternative_uvs ? ImVec2(uvs.x, uvs.w) : ImVec2(uvs.z, uvs.y),
|
||||
ImVec2(uvs.z, uvs.w)
|
||||
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:
|
||||
@ -1458,6 +1485,22 @@ inline bool AddRoundCornerRect(ImDrawList* draw_list, const ImVec2& a, const ImV
|
||||
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,
|
||||
@ -1472,247 +1515,206 @@ 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)
|
||||
|
||||
// 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 + 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 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 - 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);
|
||||
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 + 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
|
||||
const int vtcs = fill ? 16 : 24;
|
||||
const int idcs = fill ? (18 * 3) : (16 * 3);
|
||||
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;
|
||||
|
||||
// Write a vertex to the draw list, with d being the vertex index,
|
||||
// p the position and i the index into the UV list
|
||||
#define VTX_WRITE(d, p, i) \
|
||||
draw_list->_VtxWritePtr[d].pos = (p); \
|
||||
draw_list->_VtxWritePtr[d].uv = corner_uv[(i)]; \
|
||||
// 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 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_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); \
|
||||
// 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
|
||||
|
||||
#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 = use_alternative_uvs ? \
|
||||
ImVec2(corner_uv[0].x, ImLerp(corner_uv[0].y, corner_uv[b##corner ? 2 : 1].y, py)) : \
|
||||
ImVec2(ImLerp(corner_uv[0].x, corner_uv[b##corner ? 2 : 1].x, py), corner_uv[0].y); \
|
||||
// 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
|
||||
|
||||
// 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;
|
||||
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 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)); \
|
||||
#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
|
||||
|
||||
// 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)
|
||||
{
|
||||
// Each corner emits two triangles for the corner itself,
|
||||
// and two triangles that form "wings" attached to the corner
|
||||
// Each wing forms one half of the edge (with the wing from the
|
||||
// adjacent corner forming the other)
|
||||
VTX_WRITE_FILL(dv + 0, ia);
|
||||
VTX_WRITE_FILL(dv + 1, ib);
|
||||
VTX_WRITE_FILL(dv + 2, ic);
|
||||
VTX_WRITE_FILL(dv + 3, id);
|
||||
|
||||
// Central section
|
||||
IDX_WRITE_TRI(via, vic, vib);
|
||||
IDX_WRITE_TRI(via, vic, vid);
|
||||
|
||||
// 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);
|
||||
IDX_WRITE_TRI(dv + 0, dv + 1, dv + 2);
|
||||
IDX_WRITE_TRI(dv + 0, dv + 2, dv + 3);
|
||||
dv += 4;
|
||||
}
|
||||
|
||||
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)
|
||||
//draw_list->PrimUnreserve(idcs - di, vtcs - dv);
|
||||
|
||||
#undef SNAP_POS
|
||||
#undef IDX_WRITE_TRI
|
||||
#undef VTX_WRITE
|
||||
#undef VTX_WRITE_LERPED
|
||||
#undef VTX_WRITE_LERPED_X
|
||||
#undef VTX_WRITE_LERPED_Y
|
||||
#undef VTX_WRITE_CORNER
|
||||
#undef VTX_WRITE_EDGE
|
||||
#undef VTX_WRITE_FILL
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -3356,41 +3358,47 @@ static void ImFontAtlasBuildRegisterRoundCornersCustomRects(ImFontAtlas* atlas)
|
||||
const unsigned int max_radius = ImFontAtlasRoundCornersMaxSize;
|
||||
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 stroke_width_index = 0; stroke_width_index < max_thickness; stroke_width_index++)
|
||||
for (unsigned int radius_index = 0; radius_index < max_radius; radius_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))
|
||||
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++)
|
||||
{
|
||||
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);
|
||||
corner_data.StrokedUsesAlternateUVs = false;
|
||||
if (stroke_width_index != 0)
|
||||
spare_rect_id = corner_data.RectId;
|
||||
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
|
||||
{
|
||||
// Pack this into the spare half of the previous rect
|
||||
corner_data.RectId = spare_rect_id;
|
||||
corner_data.StrokedUsesAlternateUVs = true;
|
||||
spare_rect_id = -1;
|
||||
corner_data.RectId = -1; // Set RectId to -1 if we don't want any data
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
corner_data.RectId = -1; // Set RectId to -1 if we don't want any data
|
||||
}
|
||||
|
||||
IM_ASSERT_PARANOID(atlas->TexRoundCornerData.Size == (int)index);
|
||||
atlas->TexRoundCornerData.push_back(corner_data);
|
||||
ImVector<ImFontRoundedCornerData>& data_list = square ? atlas->TexSquareCornerData : atlas->TexRoundCornerData;
|
||||
|
||||
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 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 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_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++)
|
||||
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;
|
||||
data_list.reserve(max_radius * max_thickness);
|
||||
|
||||
ImFontRoundedCornerData& data = atlas->TexRoundCornerData[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)
|
||||
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++)
|
||||
{
|
||||
// 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 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)
|
||||
{
|
||||
const unsigned int candidate_index = stroke_width_index + (radius_index * ImFontAtlasRoundCornersMaxStrokeWidth);
|
||||
ImFontRoundedCornerData* candidate_data = &atlas->TexRoundCornerData[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;
|
||||
}
|
||||
|
||||
// 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++;
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
while (stroke_width_index < max_thickness)
|
||||
{
|
||||
// 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 unsigned int candidate_index = stroke_width_index + (radius_index * ImFontAtlasRoundCornersMaxStrokeWidth);
|
||||
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));
|
||||
float alpha = 0.0f;
|
||||
if (filled)
|
||||
{
|
||||
if (stroke_width_index > 0)
|
||||
if (candidate_data->RectId == data.RectId)
|
||||
{
|
||||
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
|
||||
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);
|
||||
const float alpha2 = ImClamp(other_dist, 0.0f, 1.0f);
|
||||
alpha = alpha1 - alpha2;
|
||||
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
|
||||
{
|
||||
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
|
||||
{
|
||||
const float alpha1 = ImClamp(dist + stroke_width, 0.0f, 1.0f);
|
||||
const float alpha2 = ImClamp(dist, 0.0f, 1.0f);
|
||||
alpha = alpha1 - alpha2;
|
||||
data.TexUvStroked = ImVec4(uv0.x, uv0.y, uv1.x, uv1.y);
|
||||
}
|
||||
|
||||
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.
|
||||
|
@ -726,6 +726,7 @@ struct IMGUI_API ImDrawListSharedData
|
||||
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);
|
||||
|
Loading…
Reference in New Issue
Block a user