mirror of
https://github.com/Drezil/imgui.git
synced 2024-11-26 21:47:00 +00:00
Texture-based round corners: Optimized texture-based rounded rectangle/circle code for better CPU performance at the cost of fill-rate
This commit is contained in:
parent
edb797538d
commit
ed1e598ebe
489
imgui_draw.cpp
489
imgui_draw.cpp
@ -1420,11 +1420,6 @@ inline bool AddRoundCornerRect(ImDrawList* draw_list, const ImVec2& a, const ImV
|
|||||||
ImTextureID tex_id = data->Font->ContainerAtlas->TexID;
|
ImTextureID tex_id = data->Font->ContainerAtlas->TexID;
|
||||||
IM_ASSERT(tex_id == draw_list->_TextureIdStack.back()); // Use high-level ImGui::PushFont() or low-level ImDrawList::PushTextureId() to change font.
|
IM_ASSERT(tex_id == draw_list->_TextureIdStack.back()); // Use high-level ImGui::PushFont() or low-level ImDrawList::PushTextureId() to change font.
|
||||||
|
|
||||||
// The width of our line for unfilled mode
|
|
||||||
// Something of a placeholder at the moment - used for calculations but without appropriately-generated
|
|
||||||
// textures won't actually achieve anything
|
|
||||||
const float line_width = 1.0f;
|
|
||||||
|
|
||||||
// Calculate UVs for the three points we are interested in from the texture
|
// Calculate UVs for the three points we are interested in from the texture
|
||||||
// corner_uv[0] is the innermost point of the circle (solid for filled circles)
|
// corner_uv[0] is the innermost point of the circle (solid for filled circles)
|
||||||
// corner_uv[1] is either straight down or across from it (depending on if we are using the filled or stroked version)
|
// corner_uv[1] is either straight down or across from it (depending on if we are using the filled or stroked version)
|
||||||
@ -1451,12 +1446,6 @@ 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;
|
||||||
|
|
||||||
// The radius of each corner section (0 if it is not rounded)
|
|
||||||
const int rad_a = ba ? rad : 0;
|
|
||||||
const int rad_b = bb ? rad : 0;
|
|
||||||
const int rad_c = bc ? rad : 0;
|
|
||||||
const int rad_d = bd ? rad : 0;
|
|
||||||
|
|
||||||
// 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,
|
||||||
@ -1471,33 +1460,20 @@ 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)
|
|
||||||
|
|
||||||
const ImVec2 ca(a.x, a.y), cb(b.x, a.y);
|
const ImVec2 ca(a.x, a.y), cb(b.x, a.y);
|
||||||
const ImVec2 may(ca.x + rad_a, ca.y), mby(cb.x - rad_b, cb.y);
|
const ImVec2 may(ca.x + rad, ca.y), mby(cb.x - rad, cb.y);
|
||||||
const ImVec2 may2(may.x, may.y + line_width), mby2(mby.x, mby.y + line_width);
|
const ImVec2 max(ca.x, ca.y + rad), mbx(cb.x, cb.y + rad);
|
||||||
const ImVec2 max(ca.x, ca.y + rad_a), mbx(cb.x, cb.y + rad_b);
|
const ImVec2 ia(ca.x + rad, ca.y + rad), ib(cb.x - rad, cb.y + rad);
|
||||||
const ImVec2 max2(max.x + line_width, max.y), mbx2(mbx.x - line_width, mbx.y);
|
|
||||||
const ImVec2 ia(ca.x + rad_a, ca.y + rad_a), ib(cb.x - rad_b, cb.y + rad_b);
|
|
||||||
|
|
||||||
const ImVec2 cc(b.x, b.y), cd(a.x, b.y);
|
const ImVec2 cc(b.x, b.y), cd(a.x, b.y);
|
||||||
const ImVec2 mdx(cd.x, cd.y - rad_d), mcx(cc.x, cc.y - rad_c);
|
const ImVec2 mdx(cd.x, cd.y - rad), mcx(cc.x, cc.y - rad);
|
||||||
const ImVec2 mdx2(mdx.x + line_width, mdx.y), mcx2(mcx.x - line_width, mcx.y);
|
const ImVec2 mdy(cd.x + rad, cd.y), mcy(cc.x - rad, cc.y);
|
||||||
const ImVec2 mdy(cd.x + rad_d, cd.y), mcy(cc.x - rad_c, cc.y);
|
const ImVec2 id(cd.x + rad, cd.y - rad), ic(cc.x - rad, cc.y - rad);
|
||||||
const ImVec2 mdy2(mdy.x, mdy.y - line_width), mcy2(mcy.x, mcy.y - line_width);
|
|
||||||
const ImVec2 id(cd.x + rad_d, cd.y - rad_d), ic(cc.x - rad_c, cc.y - rad_c);
|
|
||||||
|
|
||||||
// Generate points CA2-CD2, which are the corner points but inset towards the center by the line width
|
// Reserve enough space for the vertices/indices
|
||||||
// These are used as edge line end points when rendering un-rounded corners in non-filled mode
|
const int vtcs = 16;
|
||||||
|
const int idcs = fill ? (18 * 3) : (16 * 3);
|
||||||
const ImVec2 ca2(ca.x + line_width, ca.y + line_width), cb2(cb.x - line_width, cb.y + line_width);
|
|
||||||
const ImVec2 cc2(cc.x - line_width, cc.y - line_width), cd2(cd.x + line_width, cd.y - line_width);
|
|
||||||
|
|
||||||
// Reserve enough space for the worse-case vertex/index count (we give back any left over space later)
|
|
||||||
const int vtcs = 48;
|
|
||||||
const int idcs = 32 * 3;
|
|
||||||
draw_list->PrimReserve(idcs, vtcs);
|
draw_list->PrimReserve(idcs, vtcs);
|
||||||
|
|
||||||
const ImDrawIdx idx = (ImDrawIdx)draw_list->_VtxCurrentIdx;
|
const ImDrawIdx idx = (ImDrawIdx)draw_list->_VtxCurrentIdx;
|
||||||
@ -1509,32 +1485,6 @@ inline bool AddRoundCornerRect(ImDrawList* draw_list, const ImVec2& a, const ImV
|
|||||||
draw_list->_VtxWritePtr[d].uv = corner_uv[(i)]; \
|
draw_list->_VtxWritePtr[d].uv = corner_uv[(i)]; \
|
||||||
draw_list->_VtxWritePtr[d].col = col
|
draw_list->_VtxWritePtr[d].col = col
|
||||||
|
|
||||||
// Write a vertex using an interpolated position and UVs, where
|
|
||||||
// px and py are the parametric position within the corner
|
|
||||||
// (0,0 at the inside, 1,1 at the outside).
|
|
||||||
// "inside" here corresponds to ia/ib/ic/id, whilst "outside" is ca/cb/cc/cd
|
|
||||||
// Corner gives the corner (a/b/c/d) to use
|
|
||||||
// d is the vertex index to write to
|
|
||||||
// The px<py check is necessary because we need to mirror the texture across
|
|
||||||
// the diagonal (as we only have 45 degrees' worth of actual valid pixel data)
|
|
||||||
// This needs to be done the opposite way around for filled vs unfilled as they
|
|
||||||
// each occupy one side of the texture
|
|
||||||
#define VTX_WRITE_LERPED(d, corner, px, py) \
|
|
||||||
draw_list->_VtxWritePtr[d].pos = ImVec2(ImLerp(i##corner.x, c##corner.x, px), ImLerp(i##corner.y, c##corner.y, py)); \
|
|
||||||
draw_list->_VtxWritePtr[d].uv = ((px < py) ^ fill) ? \
|
|
||||||
ImVec2(ImLerp(corner_uv[0].x, corner_uv[b##corner ? 2 : 1].x, py), ImLerp(corner_uv[0].y, corner_uv[b##corner ? 2 : 1].y, px)) : \
|
|
||||||
ImVec2(ImLerp(corner_uv[0].x, corner_uv[b##corner ? 2 : 1].x, px), ImLerp(corner_uv[0].y, corner_uv[b##corner ? 2 : 1].y, py)); \
|
|
||||||
draw_list->_VtxWritePtr[d].col = col
|
|
||||||
|
|
||||||
// As VTX_WRITE_LERPED, but with the ability to give a custom position that overrides the position lerping
|
|
||||||
// (so effectively we only do the UV calculation)
|
|
||||||
#define VTX_WRITE_LERPED_CUSTOM_POS(d, corner, px, py, custom_pos) \
|
|
||||||
draw_list->_VtxWritePtr[d].pos = custom_pos; \
|
|
||||||
draw_list->_VtxWritePtr[d].uv = ((px < py) ^ fill) ? \
|
|
||||||
ImVec2(ImLerp(corner_uv[0].x, corner_uv[b##corner ? 2 : 1].x, py), ImLerp(corner_uv[0].y, corner_uv[b##corner ? 2 : 1].y, px)) : \
|
|
||||||
ImVec2(ImLerp(corner_uv[0].x, corner_uv[b##corner ? 2 : 1].x, px), ImLerp(corner_uv[0].y, corner_uv[b##corner ? 2 : 1].y, py)); \
|
|
||||||
draw_list->_VtxWritePtr[d].col = col
|
|
||||||
|
|
||||||
// Set up the outer corners (vca-vcd being the four outermost corners)
|
// 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.
|
// 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;
|
const int vca = 0, vcb = 1, vcc = 2, vcd = 3;
|
||||||
@ -1592,146 +1542,6 @@ inline bool AddRoundCornerRect(ImDrawList* draw_list, const ImVec2& a, const ImV
|
|||||||
VTX_WRITE(vid, id, 0);
|
VTX_WRITE(vid, id, 0);
|
||||||
dv += 12;
|
dv += 12;
|
||||||
|
|
||||||
// The unfilled version needs these vertices for the edges and thin sections at the sides of the rounded corners
|
|
||||||
// If a corner is not rounded then we calculate the UVs as normal (necessary to make the edges work), but
|
|
||||||
// push the actual vertex position up into the corner
|
|
||||||
|
|
||||||
int vya2 = vca, vxa2 = vca;
|
|
||||||
int vyb2 = vcb, vxb2 = vcb;
|
|
||||||
int vyc2 = vcc, vxc2 = vcc;
|
|
||||||
int vyd2 = vcd, vxd2 = vcd;
|
|
||||||
|
|
||||||
const float width_offset_parametric = line_width / rad; // Line width in our parametric coordinate space
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
if (ba)
|
|
||||||
{
|
|
||||||
VTX_WRITE_LERPED(vxa2, a, 1.0f - width_offset_parametric, 0.0f);
|
|
||||||
VTX_WRITE_LERPED(vya2, a, 0.0f, 1.0f - width_offset_parametric);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
VTX_WRITE_LERPED_CUSTOM_POS(vxa2, a, 1.0f - width_offset_parametric, 0.0f, ca2);
|
|
||||||
VTX_WRITE_LERPED_CUSTOM_POS(vya2, a, 0.0f, 1.0f - width_offset_parametric, ca2);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bb)
|
|
||||||
{
|
|
||||||
VTX_WRITE_LERPED(vxb2, b, 1.0f - width_offset_parametric, 0.0f);
|
|
||||||
VTX_WRITE_LERPED(vyb2, b, 0.0f, 1.0f - width_offset_parametric);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
VTX_WRITE_LERPED_CUSTOM_POS(vxb2, b, 1.0f - width_offset_parametric, 0.0f, cb2);
|
|
||||||
VTX_WRITE_LERPED_CUSTOM_POS(vyb2, b, 0.0f, 1.0f - width_offset_parametric, cb2);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bc)
|
|
||||||
{
|
|
||||||
VTX_WRITE_LERPED(vxc2, c, 1.0f - width_offset_parametric, 0.0f);
|
|
||||||
VTX_WRITE_LERPED(vyc2, c, 0.0f, 1.0f - width_offset_parametric);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
VTX_WRITE_LERPED_CUSTOM_POS(vxc2, c, 1.0f - width_offset_parametric, 0.0f, cc2);
|
|
||||||
VTX_WRITE_LERPED_CUSTOM_POS(vyc2, c, 0.0f, 1.0f - width_offset_parametric, cc2);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bd)
|
|
||||||
{
|
|
||||||
VTX_WRITE_LERPED(vxd2, d, 1.0f - width_offset_parametric, 0.0f);
|
|
||||||
VTX_WRITE_LERPED(vyd2, d, 0.0f, 1.0f - width_offset_parametric);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
VTX_WRITE_LERPED_CUSTOM_POS(vxd2, d, 1.0f - width_offset_parametric, 0.0f, cd2);
|
|
||||||
VTX_WRITE_LERPED_CUSTOM_POS(vyd2, d, 0.0f, 1.0f - width_offset_parametric, cd2);
|
|
||||||
}
|
|
||||||
|
|
||||||
dv += 8;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extra vertices for clipping the corners
|
|
||||||
// These form points on the inside/outside of the curve
|
|
||||||
//
|
|
||||||
// OC points are the outside of the point in the "middle" of the curve (i.e. at the 45 degree mark)
|
|
||||||
// OI are the inside of the same point (i.e. OC and OI are a line width apart)
|
|
||||||
// OX is the outer point half-way between OC and VX, sufficiently "out" that neither the OC-OX nor VX-OX
|
|
||||||
// lines intersect the curve (i.e. the intersection between the tangent to the curve at the 45 degree point
|
|
||||||
// and the tangent at VX)
|
|
||||||
// OY is the same on the VY side of the curve
|
|
||||||
|
|
||||||
int oxa = vca, oya = vca, oca = vca, oia = vca;
|
|
||||||
int oxb = vcb, oyb = vcb, ocb = vcb, oib = vcb;
|
|
||||||
int oxc = vcc, oyc = vcc, occ = vcc, oic = vcc;
|
|
||||||
int oxd = vcd, oyd = vcd, ocd = vcd, oid = vcd;
|
|
||||||
|
|
||||||
// A couple of useful constants for our calculations
|
|
||||||
const float half_sqrt_two = 0.70710678f; // sqrtf(2.0f) * 0.5f
|
|
||||||
const float sqrt_two_minus_one = 0.41421356f; // sqrt(2.0f) - 1.0f
|
|
||||||
|
|
||||||
if (ba)
|
|
||||||
{
|
|
||||||
oxa = dv;
|
|
||||||
oya = dv + 1;
|
|
||||||
oca = dv + 2;
|
|
||||||
oia = dv + 3;
|
|
||||||
VTX_WRITE_LERPED(oxa, a, 1.0f, sqrt_two_minus_one);
|
|
||||||
VTX_WRITE_LERPED(oya, a, sqrt_two_minus_one, 1.0f);
|
|
||||||
VTX_WRITE_LERPED(oca, a, half_sqrt_two + width_offset_parametric, half_sqrt_two + width_offset_parametric);
|
|
||||||
VTX_WRITE_LERPED(oia, a, half_sqrt_two - width_offset_parametric, half_sqrt_two - width_offset_parametric);
|
|
||||||
dv += 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bb)
|
|
||||||
{
|
|
||||||
oxb = dv;
|
|
||||||
oyb = dv + 1;
|
|
||||||
ocb = dv + 2;
|
|
||||||
oib = dv + 3;
|
|
||||||
VTX_WRITE_LERPED(oxb, b, 1.0f, sqrt_two_minus_one);
|
|
||||||
VTX_WRITE_LERPED(oyb, b, sqrt_two_minus_one, 1.0f);
|
|
||||||
VTX_WRITE_LERPED(ocb, b, half_sqrt_two + width_offset_parametric, half_sqrt_two + width_offset_parametric);
|
|
||||||
VTX_WRITE_LERPED(oib, b, half_sqrt_two - width_offset_parametric, half_sqrt_two - width_offset_parametric);
|
|
||||||
dv += 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bc)
|
|
||||||
{
|
|
||||||
oxc = dv;
|
|
||||||
oyc = dv + 1;
|
|
||||||
occ = dv + 2;
|
|
||||||
oic = dv + 3;
|
|
||||||
VTX_WRITE_LERPED(oxc, c, 1.0f, sqrt_two_minus_one);
|
|
||||||
VTX_WRITE_LERPED(oyc, c, sqrt_two_minus_one, 1.0f);
|
|
||||||
VTX_WRITE_LERPED(occ, c, half_sqrt_two + width_offset_parametric, half_sqrt_two + width_offset_parametric);
|
|
||||||
VTX_WRITE_LERPED(oic, c, half_sqrt_two - width_offset_parametric, half_sqrt_two - width_offset_parametric);
|
|
||||||
dv += 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bd)
|
|
||||||
{
|
|
||||||
oxd = dv;
|
|
||||||
oyd = dv + 1;
|
|
||||||
ocd = dv + 2;
|
|
||||||
oid = dv + 3;
|
|
||||||
VTX_WRITE_LERPED(oxd, d, 1.0f, sqrt_two_minus_one);
|
|
||||||
VTX_WRITE_LERPED(oyd, d, sqrt_two_minus_one, 1.0f);
|
|
||||||
VTX_WRITE_LERPED(ocd, d, half_sqrt_two + width_offset_parametric, half_sqrt_two + width_offset_parametric);
|
|
||||||
VTX_WRITE_LERPED(oid, d, half_sqrt_two - width_offset_parametric, half_sqrt_two - width_offset_parametric);
|
|
||||||
dv += 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Here we emit the actual triangles
|
// 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
|
||||||
@ -1755,73 +1565,33 @@ inline bool AddRoundCornerRect(ImDrawList* draw_list, const ImVec2& a, const ImV
|
|||||||
IDX_WRITE_TRI(via, vic, vib);
|
IDX_WRITE_TRI(via, vic, vib);
|
||||||
IDX_WRITE_TRI(via, vic, vid);
|
IDX_WRITE_TRI(via, vic, vid);
|
||||||
|
|
||||||
// Corner
|
// Top-left corner
|
||||||
if (ba)
|
|
||||||
{
|
|
||||||
IDX_WRITE_TRI(via, vxa, oxa);
|
|
||||||
IDX_WRITE_TRI(via, oxa, oca);
|
|
||||||
IDX_WRITE_TRI(via, oca, oya);
|
|
||||||
IDX_WRITE_TRI(via, oya, vya);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
IDX_WRITE_TRI(vca, vya, via);
|
IDX_WRITE_TRI(vca, vya, via);
|
||||||
IDX_WRITE_TRI(vca, vxa, via);
|
IDX_WRITE_TRI(vca, vxa, via);
|
||||||
}
|
|
||||||
|
|
||||||
// Edge "wing"
|
// Edge "wing"
|
||||||
IDX_WRITE_TRI(vib, vya, via);
|
IDX_WRITE_TRI(vib, vya, via);
|
||||||
IDX_WRITE_TRI(vid, vxa, via);
|
IDX_WRITE_TRI(vid, vxa, via);
|
||||||
|
|
||||||
// Corner
|
// Top-right corner
|
||||||
if (bb)
|
|
||||||
{
|
|
||||||
IDX_WRITE_TRI(vib, vxb, oxb);
|
|
||||||
IDX_WRITE_TRI(vib, oxb, ocb);
|
|
||||||
IDX_WRITE_TRI(vib, ocb, oyb);
|
|
||||||
IDX_WRITE_TRI(vib, oyb, vyb);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
IDX_WRITE_TRI(vcb, vyb, vib);
|
IDX_WRITE_TRI(vcb, vyb, vib);
|
||||||
IDX_WRITE_TRI(vcb, vxb, vib);
|
IDX_WRITE_TRI(vcb, vxb, vib);
|
||||||
}
|
|
||||||
|
|
||||||
// Edge "wing"
|
// Edge "wing"
|
||||||
IDX_WRITE_TRI(vya, vyb, vib);
|
IDX_WRITE_TRI(vya, vyb, vib);
|
||||||
IDX_WRITE_TRI(vic, vxb, vib);
|
IDX_WRITE_TRI(vic, vxb, vib);
|
||||||
|
|
||||||
// Corner
|
// Bottom-right corner
|
||||||
if (bc)
|
|
||||||
{
|
|
||||||
IDX_WRITE_TRI(vic, vxc, oxc);
|
|
||||||
IDX_WRITE_TRI(vic, oxc, occ);
|
|
||||||
IDX_WRITE_TRI(vic, occ, oyc);
|
|
||||||
IDX_WRITE_TRI(vic, oyc, vyc);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
IDX_WRITE_TRI(vcc, vyc, vic);
|
IDX_WRITE_TRI(vcc, vyc, vic);
|
||||||
IDX_WRITE_TRI(vcc, vxc, vic);
|
IDX_WRITE_TRI(vcc, vxc, vic);
|
||||||
}
|
|
||||||
|
|
||||||
// Edge "wing"
|
// Edge "wing"
|
||||||
IDX_WRITE_TRI(vxb, vxc, vic);
|
IDX_WRITE_TRI(vxb, vxc, vic);
|
||||||
IDX_WRITE_TRI(vyd, vyc, vic);
|
IDX_WRITE_TRI(vyd, vyc, vic);
|
||||||
|
|
||||||
// Corner
|
// Bottom-left corner
|
||||||
if (bd)
|
|
||||||
{
|
|
||||||
IDX_WRITE_TRI(vid, vxd, oxd);
|
|
||||||
IDX_WRITE_TRI(vid, oxd, ocd);
|
|
||||||
IDX_WRITE_TRI(vid, ocd, oyd);
|
|
||||||
IDX_WRITE_TRI(vid, oyd, vyd);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
IDX_WRITE_TRI(vcd, vyd, vid);
|
IDX_WRITE_TRI(vcd, vyd, vid);
|
||||||
IDX_WRITE_TRI(vcd, vxd, vid);
|
IDX_WRITE_TRI(vcd, vxd, vid);
|
||||||
}
|
|
||||||
|
|
||||||
// Edge "wing"
|
// Edge "wing"
|
||||||
IDX_WRITE_TRI(vic, vyd, vid);
|
IDX_WRITE_TRI(vic, vyd, vid);
|
||||||
@ -1832,78 +1602,53 @@ inline bool AddRoundCornerRect(ImDrawList* draw_list, const ImVec2& a, const ImV
|
|||||||
// Unfilled version
|
// Unfilled version
|
||||||
|
|
||||||
// Top edge
|
// Top edge
|
||||||
IDX_WRITE_TRI(vya, vya2, vyb2);
|
IDX_WRITE_TRI(vya, via, vib);
|
||||||
IDX_WRITE_TRI(vya, vyb, vyb2);
|
IDX_WRITE_TRI(vya, vyb, vib);
|
||||||
|
|
||||||
// Bottom edge
|
// Bottom edge
|
||||||
IDX_WRITE_TRI(vyd, vyd2, vyc2);
|
IDX_WRITE_TRI(vyd, vid, vic);
|
||||||
IDX_WRITE_TRI(vyd, vyc, vyc2);
|
IDX_WRITE_TRI(vyd, vyc, vic);
|
||||||
|
|
||||||
// Left edge
|
// Left edge
|
||||||
IDX_WRITE_TRI(vxa, vxa2, vxd2);
|
IDX_WRITE_TRI(vxa, via, vid);
|
||||||
IDX_WRITE_TRI(vxa, vxd, vxd2);
|
IDX_WRITE_TRI(vxa, vxd, vid);
|
||||||
|
|
||||||
// Right edge
|
// Right edge
|
||||||
IDX_WRITE_TRI(vxb, vxb2, vxc2);
|
IDX_WRITE_TRI(vxb, vib, vic);
|
||||||
IDX_WRITE_TRI(vxb, vxc, vxc2);
|
IDX_WRITE_TRI(vxb, vxc, vic);
|
||||||
|
|
||||||
// Corners
|
// Corners
|
||||||
|
|
||||||
if (ba)
|
// Top-left
|
||||||
{
|
IDX_WRITE_TRI(vca, vya, via);
|
||||||
IDX_WRITE_TRI(vxa, vxa2, oxa);
|
IDX_WRITE_TRI(vca, vxa, via);
|
||||||
IDX_WRITE_TRI(oxa, vxa2, oia);
|
|
||||||
IDX_WRITE_TRI(oxa, oca, oia);
|
|
||||||
IDX_WRITE_TRI(oca, oya, oia);
|
|
||||||
IDX_WRITE_TRI(oya, vya2, oia);
|
|
||||||
IDX_WRITE_TRI(oya, vya, vya2);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bb)
|
// Top-right
|
||||||
{
|
IDX_WRITE_TRI(vcb, vyb, vib);
|
||||||
IDX_WRITE_TRI(vxb, vxb2, oxb);
|
IDX_WRITE_TRI(vcb, vxb, vib);
|
||||||
IDX_WRITE_TRI(oxb, vxb2, oib);
|
|
||||||
IDX_WRITE_TRI(oxb, ocb, oib);
|
|
||||||
IDX_WRITE_TRI(ocb, oyb, oib);
|
|
||||||
IDX_WRITE_TRI(oyb, vyb2, oib);
|
|
||||||
IDX_WRITE_TRI(oyb, vyb, vyb2);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bc)
|
// Bottom-right
|
||||||
{
|
IDX_WRITE_TRI(vcc, vyc, vic);
|
||||||
IDX_WRITE_TRI(vxc, vxc2, oxc);
|
IDX_WRITE_TRI(vcc, vxc, vic);
|
||||||
IDX_WRITE_TRI(oxc, vxc2, oic);
|
|
||||||
IDX_WRITE_TRI(oxc, occ, oic);
|
|
||||||
IDX_WRITE_TRI(occ, oyc, oic);
|
|
||||||
IDX_WRITE_TRI(oyc, vyc2, oic);
|
|
||||||
IDX_WRITE_TRI(oyc, vyc, vyc2);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bd)
|
// Bottom-left
|
||||||
{
|
IDX_WRITE_TRI(vcd, vyd, vid);
|
||||||
IDX_WRITE_TRI(vxd, vxd2, oxd);
|
IDX_WRITE_TRI(vcd, vxd, vid);
|
||||||
IDX_WRITE_TRI(oxd, vxd2, oid);
|
|
||||||
IDX_WRITE_TRI(oxd, ocd, oid);
|
|
||||||
IDX_WRITE_TRI(ocd, oyd, oid);
|
|
||||||
IDX_WRITE_TRI(oyd, vyd2, oid);
|
|
||||||
IDX_WRITE_TRI(oyd, vyd, vyd2);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
draw_list->_VtxWritePtr += dv;
|
draw_list->_VtxWritePtr += dv;
|
||||||
draw_list->_VtxCurrentIdx += dv;
|
draw_list->_VtxCurrentIdx += dv;
|
||||||
draw_list->_IdxWritePtr += di;
|
draw_list->_IdxWritePtr += di;
|
||||||
|
|
||||||
IM_ASSERT_PARANOID(di <= idcs);
|
IM_ASSERT_PARANOID(di == idcs);
|
||||||
IM_ASSERT_PARANOID(dv <= vtcs);
|
IM_ASSERT_PARANOID(dv == vtcs);
|
||||||
|
|
||||||
// Return any unused vertices/indices
|
// Return any unused vertices/indices
|
||||||
draw_list->PrimUnreserve(idcs - di, vtcs - dv);
|
// (not required ATM as we always generate the right number)
|
||||||
|
//draw_list->PrimUnreserve(idcs - di, vtcs - dv);
|
||||||
|
|
||||||
#undef IDX_WRITE_TRI
|
#undef IDX_WRITE_TRI
|
||||||
#undef VTX_WRITE
|
#undef VTX_WRITE
|
||||||
#undef VTX_WRITE_LERPED
|
|
||||||
#undef VTX_WRITE_LERPED_CUSTOM_POS
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -2072,50 +1817,13 @@ inline bool AddRoundCornerCircle(ImDrawList* draw_list, const ImVec2& center, fl
|
|||||||
ImVec2 tl = ImVec2(c.x - rad, c.y - rad);
|
ImVec2 tl = ImVec2(c.x - rad, c.y - rad);
|
||||||
ImVec2 br = ImVec2(c.x + rad, c.y + rad);
|
ImVec2 br = ImVec2(c.x + rad, c.y + rad);
|
||||||
|
|
||||||
// A couple of useful constants for our calculations
|
// Some useful constants for our calculations
|
||||||
const float half_sqrt_two = 0.70710678f; // sqrtf(2.0f) * 0.5f
|
const float half_sqrt_two = 0.70710678f; // sqrtf(2.0f) * 0.5f
|
||||||
const float sqrt_two_minus_one = 0.41421356f; // sqrt(2.0f) - 1.0f
|
const float sqrt_two_minus_one = 0.41421356f; // sqrt(2.0f) - 1.0f
|
||||||
|
|
||||||
// The positions of our intermediate vertices
|
|
||||||
// These are organized by quadrant (TL = top-left, etc),
|
|
||||||
// with A being the first point encountered when walking clockwise
|
|
||||||
// around the circle within that quadrant, B the second and C the third.
|
|
||||||
// These points form a tight fit around the outer edge of the circle.
|
|
||||||
|
|
||||||
const float width_offset_parametric = line_width / rad; // Line width in our parametric coordinate space
|
const float width_offset_parametric = line_width / rad; // Line width in our parametric coordinate space
|
||||||
|
|
||||||
const ImVec2 tla = ImVec2(tl.x, ImLerp(c.y, tl.y, sqrt_two_minus_one));
|
const int num_verts = fill ? 9 : 16; // Number of vertices we are going to write
|
||||||
const ImVec2 tlb = ImVec2(ImLerp(c.x, tl.x, half_sqrt_two + width_offset_parametric), ImLerp(c.y, tl.y, half_sqrt_two + width_offset_parametric));
|
const int num_indices = fill ? 24 : 48; // Number of indices we are going to write
|
||||||
const ImVec2 tlc = ImVec2(ImLerp(c.x, tl.x, sqrt_two_minus_one), tl.y);
|
|
||||||
const ImVec2 tra = ImVec2(ImLerp(c.x, br.x, sqrt_two_minus_one), tl.y);
|
|
||||||
const ImVec2 trb = ImVec2(ImLerp(c.x, br.x, half_sqrt_two + width_offset_parametric), ImLerp(c.y, tl.y, half_sqrt_two + width_offset_parametric));
|
|
||||||
const ImVec2 trc = ImVec2(br.x, ImLerp(c.y, tl.y, sqrt_two_minus_one));
|
|
||||||
const ImVec2 bra = ImVec2(br.x, ImLerp(c.y, br.y, sqrt_two_minus_one));
|
|
||||||
const ImVec2 brb = ImVec2(ImLerp(c.x, br.x, half_sqrt_two + width_offset_parametric), ImLerp(c.y, br.y, half_sqrt_two + width_offset_parametric));
|
|
||||||
const ImVec2 brc = ImVec2(ImLerp(c.x, br.x, sqrt_two_minus_one), br.y);
|
|
||||||
const ImVec2 bla = ImVec2(ImLerp(c.x, tl.x, sqrt_two_minus_one), br.y);
|
|
||||||
const ImVec2 blb = ImVec2(ImLerp(c.x, tl.x, half_sqrt_two + width_offset_parametric), ImLerp(c.y, br.y, half_sqrt_two + width_offset_parametric));
|
|
||||||
const ImVec2 blc = ImVec2(tl.x, ImLerp(c.y, br.y, sqrt_two_minus_one));
|
|
||||||
|
|
||||||
// UVs for the A/B/C points
|
|
||||||
|
|
||||||
// Calculate the UV for a position within a quadrant
|
|
||||||
// px and py are the parametric position within the quadrant
|
|
||||||
// (0,0 at the inside, 1,1 at the outside).
|
|
||||||
// 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 CALC_CORNER_UV(px, py) ((px < py) ^ fill) ? \
|
|
||||||
ImVec2(ImLerp(corner_uv[0].x, corner_uv[2].x, py), ImLerp(corner_uv[0].y, corner_uv[2].y, px)) : \
|
|
||||||
ImVec2(ImLerp(corner_uv[0].x, corner_uv[2].x, px), ImLerp(corner_uv[0].y, corner_uv[2].y, py))
|
|
||||||
|
|
||||||
ImVec2 uva = CALC_CORNER_UV(1.0f, sqrt_two_minus_one);
|
|
||||||
ImVec2 uvb = CALC_CORNER_UV(half_sqrt_two + width_offset_parametric, half_sqrt_two + width_offset_parametric);
|
|
||||||
ImVec2 uvc = CALC_CORNER_UV(sqrt_two_minus_one, 1.0f);
|
|
||||||
|
|
||||||
const int num_verts = fill ? 17 : 24; // Number of vertices we are going to write
|
|
||||||
const int num_indices = fill ? 48 : 72; // Number of indices we are going to write
|
|
||||||
|
|
||||||
draw_list->PrimReserve(num_indices, num_verts);
|
draw_list->PrimReserve(num_indices, num_verts);
|
||||||
|
|
||||||
@ -2130,49 +1838,42 @@ inline bool AddRoundCornerCircle(ImDrawList* draw_list, const ImVec2& center, fl
|
|||||||
|
|
||||||
// Edge vertices working around the circle clockwise from the left
|
// Edge vertices working around the circle clockwise from the left
|
||||||
VTX_WRITE(0, ImVec2(tl.x, c.y), corner_uv[1]);
|
VTX_WRITE(0, ImVec2(tl.x, c.y), corner_uv[1]);
|
||||||
VTX_WRITE(1, tla, uva);
|
VTX_WRITE(1, tl, corner_uv[2]);
|
||||||
VTX_WRITE(2, tlb, uvb);
|
VTX_WRITE(2, ImVec2(c.x, tl.y), corner_uv[1]);
|
||||||
VTX_WRITE(3, tlc, uvc);
|
VTX_WRITE(3, ImVec2(br.x, tl.y), corner_uv[2]);
|
||||||
VTX_WRITE(4, ImVec2(c.x, tl.y), corner_uv[1]);
|
VTX_WRITE(4, ImVec2(br.x, c.y), corner_uv[1]);
|
||||||
VTX_WRITE(5, tra, uva);
|
VTX_WRITE(5, br, corner_uv[2]);
|
||||||
VTX_WRITE(6, trb, uvb);
|
VTX_WRITE(6, ImVec2(c.x, br.y), corner_uv[1]);
|
||||||
VTX_WRITE(7, trc, uvc);
|
VTX_WRITE(7, ImVec2(tl.x, br.y), corner_uv[2]);
|
||||||
VTX_WRITE(8, ImVec2(br.x, c.y), corner_uv[1]);
|
|
||||||
VTX_WRITE(9, bra, uva);
|
|
||||||
VTX_WRITE(10, brb, uvb);
|
|
||||||
VTX_WRITE(11, brc, uvc);
|
|
||||||
VTX_WRITE(12, ImVec2(c.x, br.y), corner_uv[1]);
|
|
||||||
VTX_WRITE(13, bla, uva);
|
|
||||||
VTX_WRITE(14, blb, uvb);
|
|
||||||
VTX_WRITE(15, blc, uvc);
|
|
||||||
|
|
||||||
if (fill)
|
if (fill)
|
||||||
{
|
{
|
||||||
// The center
|
// The center
|
||||||
VTX_WRITE(16, c, corner_uv[0]);
|
VTX_WRITE(8, c, corner_uv[0]);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Inside vertices, offset from the "B" vertices by the line width
|
// Inside vertices on the diagonals of each quadrant
|
||||||
const ImVec2 tlbi = ImVec2(ImLerp(c.x, tl.x, half_sqrt_two - width_offset_parametric), ImLerp(c.y, tl.y, half_sqrt_two - width_offset_parametric));
|
const ImVec2 tlbi = ImVec2(ImLerp(c.x, tl.x, half_sqrt_two - width_offset_parametric), ImLerp(c.y, tl.y, half_sqrt_two - width_offset_parametric));
|
||||||
const ImVec2 trbi = ImVec2(ImLerp(c.x, br.x, half_sqrt_two - width_offset_parametric), ImLerp(c.y, tl.y, half_sqrt_two - width_offset_parametric));
|
const ImVec2 trbi = ImVec2(ImLerp(c.x, br.x, half_sqrt_two - width_offset_parametric), ImLerp(c.y, tl.y, half_sqrt_two - width_offset_parametric));
|
||||||
const ImVec2 brbi = ImVec2(ImLerp(c.x, br.x, half_sqrt_two - width_offset_parametric), ImLerp(c.y, br.y, half_sqrt_two - width_offset_parametric));
|
const ImVec2 brbi = ImVec2(ImLerp(c.x, br.x, half_sqrt_two - width_offset_parametric), ImLerp(c.y, br.y, half_sqrt_two - width_offset_parametric));
|
||||||
const ImVec2 blbi = ImVec2(ImLerp(c.x, tl.x, half_sqrt_two - width_offset_parametric), ImLerp(c.y, br.y, half_sqrt_two - width_offset_parametric));
|
const ImVec2 blbi = ImVec2(ImLerp(c.x, tl.x, half_sqrt_two - width_offset_parametric), ImLerp(c.y, br.y, half_sqrt_two - width_offset_parametric));
|
||||||
|
|
||||||
// UV for the inside "B" points
|
// UV for the inside diagonal points
|
||||||
ImVec2 uvbi = CALC_CORNER_UV(half_sqrt_two - width_offset_parametric, half_sqrt_two - width_offset_parametric);
|
ImVec2 uvbi = ImVec2(ImLerp(corner_uv[0].x, corner_uv[2].x, half_sqrt_two - width_offset_parametric), ImLerp(corner_uv[0].y, corner_uv[2].y, half_sqrt_two - width_offset_parametric));
|
||||||
|
|
||||||
// UV for the interior cardinal points
|
// UV for the interior cardinal points
|
||||||
ImVec2 uvi_cardinal = CALC_CORNER_UV(0.0f, 1.0f - width_offset_parametric);
|
ImVec2 uvi_cardinal = ImVec2(ImLerp(corner_uv[0].x, corner_uv[2].x, 1.0f - width_offset_parametric), corner_uv[0].y);
|
||||||
|
|
||||||
// Inner vertices, starting from the left
|
// Inner vertices, starting from the left
|
||||||
VTX_WRITE(16, ImVec2(tl.x + line_width, c.y), uvi_cardinal);
|
VTX_WRITE(8, ImVec2(tl.x + line_width, c.y), uvi_cardinal);
|
||||||
VTX_WRITE(17, tlbi, uvbi);
|
VTX_WRITE(9, tlbi, uvbi);
|
||||||
VTX_WRITE(18, ImVec2(c.x, tl.y + line_width), uvi_cardinal);
|
VTX_WRITE(10, ImVec2(c.x, tl.y + line_width), uvi_cardinal);
|
||||||
VTX_WRITE(19, trbi, uvbi);
|
VTX_WRITE(11, trbi, uvbi);
|
||||||
VTX_WRITE(20, ImVec2(br.x - line_width, c.y), uvi_cardinal);
|
VTX_WRITE(12, ImVec2(br.x - line_width, c.y), uvi_cardinal);
|
||||||
VTX_WRITE(21, brbi, uvbi);
|
VTX_WRITE(13, brbi, uvbi);
|
||||||
VTX_WRITE(22, ImVec2(c.x, br.y - line_width), uvi_cardinal);
|
VTX_WRITE(14, ImVec2(c.x, br.y - line_width), uvi_cardinal);
|
||||||
VTX_WRITE(23, blbi, uvbi);
|
VTX_WRITE(15, blbi, uvbi);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write indices for a triangle formed of three indices
|
// Write indices for a triangle formed of three indices
|
||||||
@ -2187,58 +1888,41 @@ inline bool AddRoundCornerCircle(ImDrawList* draw_list, const ImVec2& center, fl
|
|||||||
if (fill)
|
if (fill)
|
||||||
{
|
{
|
||||||
// A simple fan of tris from the center
|
// A simple fan of tris from the center
|
||||||
IDX_WRITE_TRI(0, 16, 0, 1);
|
IDX_WRITE_TRI(0, 8, 0, 1);
|
||||||
IDX_WRITE_TRI(3, 16, 1, 2);
|
IDX_WRITE_TRI(3, 8, 1, 2);
|
||||||
IDX_WRITE_TRI(6, 16, 2, 3);
|
IDX_WRITE_TRI(6, 8, 2, 3);
|
||||||
IDX_WRITE_TRI(9, 16, 3, 4);
|
IDX_WRITE_TRI(9, 8, 3, 4);
|
||||||
IDX_WRITE_TRI(12, 16, 4, 5);
|
IDX_WRITE_TRI(12, 8, 4, 5);
|
||||||
IDX_WRITE_TRI(15, 16, 5, 6);
|
IDX_WRITE_TRI(15, 8, 5, 6);
|
||||||
IDX_WRITE_TRI(18, 16, 6, 7);
|
IDX_WRITE_TRI(18, 8, 6, 7);
|
||||||
IDX_WRITE_TRI(21, 16, 7, 8);
|
IDX_WRITE_TRI(21, 8, 7, 0);
|
||||||
IDX_WRITE_TRI(24, 16, 8, 9);
|
|
||||||
IDX_WRITE_TRI(27, 16, 9, 10);
|
|
||||||
IDX_WRITE_TRI(30, 16, 10, 11);
|
|
||||||
IDX_WRITE_TRI(33, 16, 11, 12);
|
|
||||||
IDX_WRITE_TRI(36, 16, 12, 13);
|
|
||||||
IDX_WRITE_TRI(39, 16, 13, 14);
|
|
||||||
IDX_WRITE_TRI(42, 16, 14, 15);
|
|
||||||
IDX_WRITE_TRI(45, 16, 15, 0);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// A more complex set of triangles to form a ring
|
// A ring of inner vertices that are tight to the circle, spanning out to the four corners
|
||||||
|
|
||||||
// Top-left quadrant
|
// Top-left quadrant
|
||||||
IDX_WRITE_TRI(0, 16, 0, 1);
|
IDX_WRITE_TRI(0, 1, 0, 8);
|
||||||
IDX_WRITE_TRI(3, 16, 1, 17);
|
IDX_WRITE_TRI(3, 1, 8, 9);
|
||||||
IDX_WRITE_TRI(6, 17, 1, 2);
|
IDX_WRITE_TRI(6, 1, 9, 10);
|
||||||
IDX_WRITE_TRI(9, 17, 2, 3);
|
IDX_WRITE_TRI(9, 1, 10, 2);
|
||||||
IDX_WRITE_TRI(12, 17, 3, 18);
|
|
||||||
IDX_WRITE_TRI(15, 18, 3, 4);
|
|
||||||
|
|
||||||
// Top-right quadrant
|
// Top-right quadrant
|
||||||
IDX_WRITE_TRI(18, 18, 4, 5);
|
IDX_WRITE_TRI(12, 3, 2, 10);
|
||||||
IDX_WRITE_TRI(21, 18, 5, 19);
|
IDX_WRITE_TRI(15, 3, 10, 11);
|
||||||
IDX_WRITE_TRI(24, 19, 5, 6);
|
IDX_WRITE_TRI(18, 3, 11, 12);
|
||||||
IDX_WRITE_TRI(27, 19, 6, 7);
|
IDX_WRITE_TRI(21, 3, 12, 4);
|
||||||
IDX_WRITE_TRI(30, 19, 7, 20);
|
|
||||||
IDX_WRITE_TRI(33, 20, 7, 8);
|
|
||||||
|
|
||||||
// Bottom-right quadrant
|
// Bottom-right quadrant
|
||||||
IDX_WRITE_TRI(36, 20, 8, 9);
|
IDX_WRITE_TRI(24, 5, 4, 12);
|
||||||
IDX_WRITE_TRI(39, 20, 9, 21);
|
IDX_WRITE_TRI(27, 5, 12, 13);
|
||||||
IDX_WRITE_TRI(42, 21, 9, 10);
|
IDX_WRITE_TRI(30, 5, 13, 14);
|
||||||
IDX_WRITE_TRI(45, 21, 10, 11);
|
IDX_WRITE_TRI(33, 5, 14, 6);
|
||||||
IDX_WRITE_TRI(48, 21, 11, 22);
|
|
||||||
IDX_WRITE_TRI(51, 22, 11, 12);
|
|
||||||
|
|
||||||
// Bottom-left quadrant
|
// Bottom-left quadrant
|
||||||
IDX_WRITE_TRI(54, 22, 12, 13);
|
IDX_WRITE_TRI(36, 7, 6, 14);
|
||||||
IDX_WRITE_TRI(57, 22, 13, 23);
|
IDX_WRITE_TRI(39, 7, 14, 15);
|
||||||
IDX_WRITE_TRI(60, 23, 13, 14);
|
IDX_WRITE_TRI(42, 7, 15, 8);
|
||||||
IDX_WRITE_TRI(63, 23, 14, 15);
|
IDX_WRITE_TRI(45, 7, 8, 0);
|
||||||
IDX_WRITE_TRI(66, 23, 15, 16);
|
|
||||||
IDX_WRITE_TRI(69, 16, 15, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
draw_list->_VtxWritePtr += num_verts;
|
draw_list->_VtxWritePtr += num_verts;
|
||||||
@ -2247,7 +1931,6 @@ inline bool AddRoundCornerCircle(ImDrawList* draw_list, const ImVec2& center, fl
|
|||||||
|
|
||||||
#undef IDX_WRITE_TRI
|
#undef IDX_WRITE_TRI
|
||||||
#undef VTX_WRITE
|
#undef VTX_WRITE
|
||||||
#undef CALC_CORNER_UV
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user