Texture-based round corners: Using ImFabs() + Minor coding style fixes for consistency

This commit is contained in:
omar 2019-11-22 19:46:58 +01:00 committed by ocornut
parent 054c87fc4f
commit edb797538d

View File

@ -1489,14 +1489,13 @@ inline bool AddRoundCornerRect(ImDrawList* draw_list, const ImVec2& a, const ImV
const ImVec2 mdy2(mdy.x, mdy.y - line_width), mcy2(mcy.x, mcy.y - line_width); 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); 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 centre by the line width // Generate points CA2-CD2, which are the corner points but inset towards the center by the line width
// These are used as edge line end points when rendering un-rounded corners in non-filled mode // These are used as edge line end points when rendering un-rounded corners in non-filled mode
const ImVec2 ca2(ca.x + line_width, ca.y + line_width), cb2(cb.x - line_width, cb.y + line_width); 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); 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 // Reserve enough space for the worse-case vertex/index count (we give back any left over space later)
// (we give back any left over space later)
const int vtcs = 48; const int vtcs = 48;
const int idcs = 32 * 3; const int idcs = 32 * 3;
draw_list->PrimReserve(idcs, vtcs); draw_list->PrimReserve(idcs, vtcs);
@ -1536,10 +1535,8 @@ inline bool AddRoundCornerRect(ImDrawList* draw_list, const ImVec2& a, const ImV
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)); \ 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 draw_list->_VtxWritePtr[d].col = col
// Set up the outer corners (vca-vcd being the four outermost // Set up the outer corners (vca-vcd being the four outermost corners)
// 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;
VTX_WRITE(vca, ca, ba ? 2 : 1); VTX_WRITE(vca, ca, ba ? 2 : 1);
VTX_WRITE(vcb, cb, bb ? 2 : 1); VTX_WRITE(vcb, cb, bb ? 2 : 1);
@ -2044,19 +2041,13 @@ inline bool AddRoundCornerCircle(ImDrawList* draw_list, const ImVec2& center, fl
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.
const int rad = (int)radius; const int rad = (int)radius;
if ((rad < 1) || // Radius 0 will cause issues with the UV lookup below if ((rad < 1) || // Radius 0 will cause issues with the UV lookup below
(rad > data->Font->ContainerAtlas->RoundCornersMaxSize)) (rad > data->Font->ContainerAtlas->RoundCornersMaxSize))
{ return false; // We can't handle this
// We can't handle this
return false;
}
// Debug command to force this render path to only execute when shift is held // Debug command to force this render path to only execute when shift is held
if (!ImGui::GetIO().KeyShift) if (!ImGui::GetIO().KeyShift)
{
return false; return false;
}
// 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)
@ -2086,7 +2077,7 @@ inline bool AddRoundCornerCircle(ImDrawList* draw_list, const ImVec2& center, fl
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 // The positions of our intermediate vertices
// These are organised by quadrant (TL = top-left, etc), // These are organized by quadrant (TL = top-left, etc),
// with A being the first point encountered when walking clockwise // with A being the first point encountered when walking clockwise
// around the circle within that quadrant, B the second and C the third. // 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. // These points form a tight fit around the outer edge of the circle.
@ -2129,9 +2120,9 @@ inline bool AddRoundCornerCircle(ImDrawList* draw_list, const ImVec2& center, fl
draw_list->PrimReserve(num_indices, num_verts); draw_list->PrimReserve(num_indices, num_verts);
// Write a vertex // Write a vertex
// d is the vertex index to write to // - d is the vertex index to write to
// vert_pos is the vertex position // - vert_pos is the vertex position
// uv_coord is the UV coordinate // - uv_coord is the UV coordinate
#define VTX_WRITE(d, vert_pos, uv_coord) \ #define VTX_WRITE(d, vert_pos, uv_coord) \
draw_list->_VtxWritePtr[d].pos = vert_pos; \ draw_list->_VtxWritePtr[d].pos = vert_pos; \
draw_list->_VtxWritePtr[d].uv = uv_coord; \ draw_list->_VtxWritePtr[d].uv = uv_coord; \
@ -2157,13 +2148,12 @@ inline bool AddRoundCornerCircle(ImDrawList* draw_list, const ImVec2& center, fl
if (fill) if (fill)
{ {
// The centre // The center
VTX_WRITE(16, c, corner_uv[0]); VTX_WRITE(16, c, corner_uv[0]);
} }
else else
{ {
// Inside vertices, offset from the "B" vertices by the line width // Inside vertices, offset from the "B" vertices by the line width
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));
@ -2196,7 +2186,7 @@ inline bool AddRoundCornerCircle(ImDrawList* draw_list, const ImVec2& center, fl
if (fill) if (fill)
{ {
// A simple fan of tris from the centre // A simple fan of tris from the center
IDX_WRITE_TRI(0, 16, 0, 1); IDX_WRITE_TRI(0, 16, 0, 1);
IDX_WRITE_TRI(3, 16, 1, 2); IDX_WRITE_TRI(3, 16, 1, 2);
IDX_WRITE_TRI(6, 16, 2, 3); IDX_WRITE_TRI(6, 16, 2, 3);
@ -2268,11 +2258,8 @@ void ImDrawList::AddCircle(const ImVec2& center, float radius, ImU32 col, int nu
return; return;
// First try the fast texture-based renderer, and only if that can't handle this fall back to paths // First try the fast texture-based renderer, and only if that can't handle this fall back to paths
if (AddRoundCornerCircle(this, center, radius, col, false)) if (AddRoundCornerCircle(this, center, radius, col, false))
{
return; return;
}
// Obtain segment count // Obtain segment count
if (num_segments <= 0) if (num_segments <= 0)
@ -3582,7 +3569,7 @@ void ImFontAtlasBuildInit(ImFontAtlas* atlas)
const int FONT_ATLAS_ROUNDED_CORNER_TEX_PADDING = 2; const int FONT_ATLAS_ROUNDED_CORNER_TEX_PADDING = 2;
// Padding applied to the Y axis to separate the two halves of the image // Padding applied to the Y axis to separate the two halves of the image
// This must be a multiple of two // This must be a multiple of two
const int FONT_ATLAS_ROUNDED_CORNER_TEX_CENTRE_PADDING = 4; const int FONT_ATLAS_ROUNDED_CORNER_TEX_CENTER_PADDING = 4;
// Register the rectangles we need for the rounded corner images // Register the rectangles we need for the rounded corner images
static void ImFontAtlasBuildRegisterRoundCornersCustomRects(ImFontAtlas* atlas) static void ImFontAtlasBuildRegisterRoundCornersCustomRects(ImFontAtlas* atlas)
@ -3594,9 +3581,8 @@ static void ImFontAtlasBuildRegisterRoundCornersCustomRects(ImFontAtlas* atlas)
const int pad = FONT_ATLAS_ROUNDED_CORNER_TEX_PADDING; const int pad = FONT_ATLAS_ROUNDED_CORNER_TEX_PADDING;
const int max = atlas->RoundCornersMaxSize; const int max = atlas->RoundCornersMaxSize;
for (int n = 0; n < max; n++) for (int n = 0; n < max; n++)
atlas->RoundCornersRectIds.push_back(atlas->AddCustomRectRegular(n + 1 + pad * 2, n + 1 + FONT_ATLAS_ROUNDED_CORNER_TEX_CENTRE_PADDING + pad * 2)); atlas->RoundCornersRectIds.push_back(atlas->AddCustomRectRegular(n + 1 + pad * 2, n + 1 + FONT_ATLAS_ROUNDED_CORNER_TEX_CENTER_PADDING + pad * 2));
} }
// Generate the actual pixel data for rounded corners in the atlas // Generate the actual pixel data for rounded corners in the atlas
@ -3618,20 +3604,19 @@ static void ImFontAtlasBuildRenderRoundCornersTexData(ImFontAtlas* atlas)
IM_ASSERT((int)n < atlas->RoundCornersRectIds.Size); IM_ASSERT((int)n < atlas->RoundCornersRectIds.Size);
ImFontAtlasCustomRect& r = atlas->CustomRects[atlas->RoundCornersRectIds[id]]; ImFontAtlasCustomRect& r = atlas->CustomRects[atlas->RoundCornersRectIds[id]];
IM_ASSERT(r.IsPacked()); IM_ASSERT(r.IsPacked());
IM_ASSERT(r.Width == n + 1 + pad * 2 && r.Height == n + 1 + FONT_ATLAS_ROUNDED_CORNER_TEX_CENTRE_PADDING + pad * 2); IM_ASSERT(r.Width == n + 1 + pad * 2 && r.Height == n + 1 + FONT_ATLAS_ROUNDED_CORNER_TEX_CENTER_PADDING + pad * 2);
// What we're doing here is generating a rectangular image that contains the data for both the filled and // 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 // 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 // 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 // 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, // 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_CENTRE_PADDING pixels. // 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. // The stroked version is at the top-right of the texture, and the filled version at the bottom-left.
const int radius = (int)(r.Width - pad * 2); const int radius = (int)(r.Width - pad * 2);
const float stroke_width = 1.0f; const float stroke_width = 1.0f;
for (int y = -pad; y < (int)(radius + FONT_ATLAS_ROUNDED_CORNER_TEX_CENTRE_PADDING); y++) for (int y = -pad; y < (int)(radius + FONT_ATLAS_ROUNDED_CORNER_TEX_CENTER_PADDING); y++)
for (int x = -pad; x < (int)(radius); x++) 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 // We want the pad area to essentially contain a clamped version of the 0th row/column, so
@ -3640,14 +3625,13 @@ static void ImFontAtlasBuildRenderRoundCornersTexData(ImFontAtlas* atlas)
int cy = ImMax(y, 0); int cy = ImMax(y, 0);
// The X<Y region of the texture contains the data for filled corners, the X>Y region // 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_CENTRE_PADDING so that // 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. // each side gets a buffer zone to avoid filtering artifacts.
bool filled = x < (y - (FONT_ATLAS_ROUNDED_CORNER_TEX_CENTRE_PADDING >> 1)); const bool filled = x < (y - (FONT_ATLAS_ROUNDED_CORNER_TEX_CENTER_PADDING >> 1));
if (filled) if (filled)
{ {
// The filled version starts a little further down the texture to give us the padding in the middle. // 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_CENTRE_PADDING, 0); cy = ImMax(y - FONT_ATLAS_ROUNDED_CORNER_TEX_CENTER_PADDING, 0);
} }
const float dist = ImSqrt((float)(cx*cx+cy*cy)) - (float)(radius - (filled ? 0 : stroke_width)); const float dist = ImSqrt((float)(cx*cx+cy*cy)) - (float)(radius - (filled ? 0 : stroke_width));
@ -3668,21 +3652,19 @@ static void ImFontAtlasBuildRenderRoundCornersTexData(ImFontAtlas* atlas)
atlas->TexPixelsAlpha8[offset] = (unsigned char)(0xFF * ImSaturate(alpha)); 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 // We generate two sets of UVs for each rectangle, one for the filled portion and one for the unfilled bit.
// unfilled bit
for (unsigned int stage = 0; stage < 2; stage++) for (unsigned int stage = 0; stage < 2; stage++)
{ {
ImFontAtlasCustomRect stageRect = r; 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;
bool filled = stage == 0;
ImVec2 uv0, uv1; ImVec2 uv0, uv1;
stageRect.X += pad; atlas->CalcCustomRectUV(&stage_rect, &uv0, &uv1);
stageRect.Y += pad + (filled ? FONT_ATLAS_ROUNDED_CORNER_TEX_CENTRE_PADDING : 0);
stageRect.Width -= (pad * 2);
stageRect.Height -= (pad * 2) + FONT_ATLAS_ROUNDED_CORNER_TEX_CENTRE_PADDING;
atlas->CalcCustomRectUV(&stageRect, &uv0, &uv1);
ImVector<ImVec4>& uvs = (filled ? atlas->TexUvRoundCornerFilled : atlas->TexUvRoundCornerStroked); ImVector<ImVec4>& uvs = (filled ? atlas->TexUvRoundCornerFilled : atlas->TexUvRoundCornerStroked);
uvs.push_back(ImVec4(uv0.x, uv0.y, uv1.x, uv1.y)); uvs.push_back(ImVec4(uv0.x, uv0.y, uv1.x, uv1.y));
} }