@ -1402,7 +1402,7 @@ void ImDrawList::AddLine(const ImVec2& p1, const ImVec2& p2, ImU32 col, float th
// Returns true if the rectangle was drawn, false for some reason it couldn't
// be (in which case the caller should try again with the regular path drawing API)
// We are using the textures generated by ImFontAtlasBuildRenderRoundCornersTexData()
inline bool AddRoundCornerRect ( ImDrawList * draw_list , const ImVec2 & a , const ImVec2 & b , ImU32 col , float rounding , ImDrawFlags flags , bool fill )
inline bool AddRoundCornerRect ( ImDrawList * draw_list , const ImVec2 & a , const ImVec2 & b , ImU32 col , float rounding , float thickness , ImDrawFlags flags , bool fill )
{
if ( ! ( draw_list - > Flags & ImDrawListFlags_TexturedRoundCorners ) ) // Disabled by the draw list flags
return false ;
@ -1414,31 +1414,35 @@ inline bool AddRoundCornerRect(ImDrawList* draw_list, const ImVec2& a, const ImV
# endif
const ImDrawListSharedData * data = draw_list - > _Data ;
const int rad = ( int ) rounding ;
if ( data - > Font - > ContainerAtlas - > Flags & ImFontAtlasFlags_NoBakedRoundCorners ) // No data in font
return false ;
if ( ( rad < = 0 ) | | // Zero radius causes issues with the [rad - 1] UV lookup below
// Filled rectangles have no stroke width
const int stroke_width = fill ? 1 : ( int ) thickness ;
if ( ( stroke_width < = 0 ) | |
( stroke_width > ImFontAtlasRoundCornersMaxStrokeWidth ) )
return false ; // We can't handle this
// If we have a >1 stroke width, we actually need to increase the radius appropriately as well to match how the geometry renderer does things
const int rad = ( int ) rounding + ( stroke_width - 1 ) ;
if ( ( rad < = 0 ) | | // We don't support zero radius
( rad > ImFontAtlasRoundCornersMaxSize ) )
{
// We can't handle this
return false ;
}
return false ; // We can't handle this
// Debug command to force this render path to only execute when shift is held
if ( ! ImGui : : GetIO ( ) . KeyShift )
return false ;
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.
const unsigned int index = ( stroke_width - 1 ) + ( ( rad - 1 ) * ImFontAtlasRoundCornersMaxStrokeWidth ) ;
ImFontRoundedCornerData & round_corner_data = ( * data - > TexRoundCornerData ) [ index ] ;
// The width of our stroke for unfilled mode
// Something of a placeholder at the moment - used for calculations but without appropriately-generated
// textures won't actually achieve anything
const float stroke_width = 1.0f ;
if ( round_corner_data . RectId < 0 )
return false ; // No data for this configuration
ImFontRoundedCornerData & round_corner_data = ( * data - > TexRoundCornerData ) [ rad - 1 ] ;
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.
// 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)
@ -1447,11 +1451,14 @@ inline bool AddRoundCornerRect(ImDrawList* draw_list, const ImVec2& a, const ImV
// corner_uv[1] is always solid (either inside the circle or on the line), whilst corner_uv[2] is always blank
// This represents a 45 degree "wedge" of circle, which then gets mirrored here to produce a 90 degree curve
// See ImFontAtlasBuildRenderRoundCornersTexData() for more details of the texture contents
// 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 ] =
{
ImVec2 ( uvs . x , uvs . y ) ,
fill ? ImVec2 ( uvs . x , uvs . w ) : ImVec2 ( uvs . z , uvs . y ) ,
use_alternative_uvs ? ImVec2 ( uvs . x , uvs . w ) : ImVec2 ( uvs . z , uvs . y ) ,
ImVec2 ( uvs . z , uvs . w )
} ;
@ -1484,14 +1491,17 @@ inline bool AddRoundCornerRect(ImDrawList* draw_list, const ImVec2& a, const ImV
// 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 ) ;
// Adjust size to account for the fact that wider strokes draw "outside the box"
const float stroke_width_size_expansion = stroke_width - 1.0f ;
const 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 cc ( b . x , b . y ) , cd ( a . x , b . y ) ;
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 ) ;
@ -1524,7 +1534,7 @@ inline bool AddRoundCornerRect(ImDrawList* draw_list, const ImVec2& a, const ImV
// 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 ) ? \
draw_list - > _VtxWritePtr [ d ] . uv = ( ( px < py ) ^ use_alternative_uvs ) ? \
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
@ -1533,12 +1543,16 @@ inline bool AddRoundCornerRect(ImDrawList* draw_list, const ImVec2& a, const ImV
# define VTX_WRITE_LERPED_X(d, corner, px) \
draw_list - > _VtxWritePtr [ d ] . pos = ImVec2 ( ImLerp ( i # # corner . x , c # # corner . x , px ) , i # # corner . y ) ; \
draw_list - > _VtxWritePtr [ d ] . uv = ImVec2 ( ImLerp ( corner_uv [ 0 ] . x , corner_uv [ b # # corner ? 2 : 1 ] . x , px ) , corner_uv [ 0 ] . y ) ; \
draw_list - > _VtxWritePtr [ d ] . uv = use_alternative_uvs ? \
ImVec2 ( corner_uv [ 0 ] . x , ImLerp ( corner_uv [ 0 ] . y , corner_uv [ b # # corner ? 2 : 1 ] . y , px ) ) : \
ImVec2 ( ImLerp ( corner_uv [ 0 ] . x , corner_uv [ b # # corner ? 2 : 1 ] . x , px ) , corner_uv [ 0 ] . y ) ; \
draw_list - > _VtxWritePtr [ d ] . col = col
# define VTX_WRITE_LERPED_Y(d, corner, py) \
draw_list - > _VtxWritePtr [ d ] . pos = ImVec2 ( i # # corner . x , ImLerp ( i # # corner . y , c # # corner . y , py ) ) ; \
draw_list - > _VtxWritePtr [ d ] . uv = ImVec2 ( ImLerp ( corner_uv [ 0 ] . x , corner_uv [ b # # corner ? 2 : 1 ] . x , py ) , corner_uv [ 0 ] . y ) ; \
draw_list - > _VtxWritePtr [ d ] . 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 ) ; \
draw_list - > _VtxWritePtr [ d ] . col = col
// Set up the outer corners (vca-vcd being the four outermost corners)
@ -1760,7 +1774,7 @@ void ImDrawList::AddRect(const ImVec2& p_min, const ImVec2& p_max, ImU32 col, fl
// Try to use fast path if we can
if ( rounding > 0 )
if ( AddRoundCornerRect ( this , p_min , p_max , col , rounding , flags , /* fill */ false ) )
if ( AddRoundCornerRect ( this , p_min , p_max , col , rounding , thickness , flags , /* fill */ false ) )
return ;
if ( Flags & ImDrawListFlags_AntiAliasedLines )
@ -1789,7 +1803,7 @@ void ImDrawList::AddRectFilled(const ImVec2& p_min, const ImVec2& p_max, ImU32 c
else
{
// Try fast path first
if ( AddRoundCornerRect ( this , p_min , p_max , col , rounding , flags , /* fill */ true ) )
if ( AddRoundCornerRect ( this , p_min , p_max , col , rounding , 1.0f , flags , /* fill */ true ) )
return ;
PathRect ( p_min , p_max , rounding , flags ) ;
@ -1862,7 +1876,7 @@ void ImDrawList::AddTriangleFilled(const ImVec2& p1, const ImVec2& p2, const ImV
// Draw a circle using the rounded corner textures
// Returns true if the circle was drawn, or false if for some reason it could not be
// (in which case the caller should try the regular circle drawing code)
inline bool AddRoundCornerCircle ( ImDrawList * draw_list , const ImVec2 & center , float radius , ImU32 col , bool fill )
inline bool AddRoundCornerCircle ( ImDrawList * draw_list , const ImVec2 & center , float radius , float thickness , ImU32 col , bool fill )
{
if ( ! ( draw_list - > Flags & ImDrawListFlags_TexturedRoundCorners ) ) // Disabled by the draw list flags
return false ;
@ -1874,15 +1888,29 @@ inline bool AddRoundCornerCircle(ImDrawList* draw_list, const ImVec2& center, fl
if ( data - > Font - > ContainerAtlas - > Flags & ImFontAtlasFlags_NoBakedRoundCorners ) // No data in font
return false ;
const int rad = ( int ) radius ;
if ( rad < 1 | | rad > ImFontAtlasRoundCornersMaxSize ) // Radius 0 will cause issues with the UV lookup below
// Filled rectangles have no stroke width
const int stroke_width = fill ? 1 : ( int ) thickness ;
if ( ( stroke_width < = 0 ) | |
( stroke_width > ImFontAtlasRoundCornersMaxStrokeWidth ) )
return false ; // We can't handle this
// If we have a >1 stroke width, we actually need to increase the radius appropriately as well to match how the geometry renderer does things
const int rad = ( int ) radius + ( stroke_width - 1 ) ;
if ( ( rad < = 0 ) | | // We don't support zero radius
( rad > ImFontAtlasRoundCornersMaxSize ) )
return false ; // We can't handle this
// Debug command to force this render path to only execute when shift is held
if ( ! ImGui : : GetIO ( ) . KeyShift )
return false ;
ImFontRoundedCornerData & round_corner_data = ( * data - > TexRoundCornerData ) [ rad - 1 ] ;
const unsigned int index = ( stroke_width - 1 ) + ( ( rad - 1 ) * ImFontAtlasRoundCornersMaxStrokeWidth ) ;
ImFontRoundedCornerData & round_corner_data = ( * data - > TexRoundCornerData ) [ index ] ;
if ( round_corner_data . RectId < 0 )
return false ; // No data for this configuration
// 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)
@ -1891,17 +1919,17 @@ inline bool AddRoundCornerCircle(ImDrawList* draw_list, const ImVec2& center, fl
// corner_uv[1] is always solid (either inside the circle or on the line), whilst corner_uv[2] is always blank
// This represents a 45 degree "wedge" of circle, which then gets mirrored here to produce a 90 degree curve
// See ImFontAtlasBuildRenderRoundCornersTexData() for more details of the texture contents
// 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 ] =
{
ImVec2 ( uvs . x , uvs . y ) ,
fill ? ImVec2 ( uvs . x , uvs . w ) : ImVec2 ( uvs . z , uvs . y ) ,
ImVec2 ( uvs . z , uvs . w ) ,
use_alternative_uvs ? ImVec2 ( uvs . x , uvs . w ) : ImVec2 ( uvs . z , uvs . y ) ,
ImVec2 ( uvs . z , uvs . w )
} ;
// Our stroke width (requires a texture with the appropriate stroke width to actually do anything)
const float stroke_width = 1.0f ;
// Calculate the circle bounds
const ImVec2 & c = center ;
ImVec2 tl = ImVec2 ( c . x - rad , c . y - rad ) ;
@ -1951,17 +1979,25 @@ inline bool AddRoundCornerCircle(ImDrawList* draw_list, const ImVec2& center, fl
// UV for the inside diagonal points
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 ) ) ;
// Left/right/top/bottom interior positions
const ImVec2 lbi = ImVec2 ( ImLerp ( tl . x , c . x , width_offset_parametric ) , c . y ) ;
const ImVec2 rbi = ImVec2 ( ImLerp ( br . x , c . x , width_offset_parametric ) , c . y ) ;
const ImVec2 tbi = ImVec2 ( c . x , ImLerp ( tl . y , c . y , width_offset_parametric ) ) ;
const ImVec2 bbi = ImVec2 ( c . x , ImLerp ( br . y , c . y , width_offset_parametric ) ) ;
// UV for the interior cardinal points
ImVec2 uvi_cardinal = ImVec2 ( ImLerp ( corner_uv [ 0 ] . x , corner_uv [ 2 ] . x , 1.0f - width_offset_parametric ) , corner_uv [ 0 ] . y ) ;
ImVec2 uvi_cardinal = use_alternative_uvs ?
ImVec2 ( corner_uv [ 0 ] . x , ImLerp ( corner_uv [ 2 ] . y , corner_uv [ 0 ] . y , width_offset_parametric ) ) :
ImVec2 ( ImLerp ( corner_uv [ 2 ] . x , corner_uv [ 0 ] . x , width_offset_parametric ) , corner_uv [ 0 ] . y ) ;
// Inner vertices, starting from the left
VTX_WRITE ( 8 , ImVec2 ( tl . x + stroke_width , c . y ) , uvi_cardinal ) ;
VTX_WRITE ( 8 , lbi , uvi_cardinal ) ;
VTX_WRITE ( 9 , tlbi , uvbi ) ;
VTX_WRITE ( 10 , ImVec2 ( c . x , tl . y + stroke_width ) , uvi_cardinal ) ;
VTX_WRITE ( 10 , tbi , uvi_cardinal ) ;
VTX_WRITE ( 11 , trbi , uvbi ) ;
VTX_WRITE ( 12 , ImVec2 ( br . x - stroke_width , c . y ) , uvi_cardinal ) ;
VTX_WRITE ( 12 , rbi , uvi_cardinal ) ;
VTX_WRITE ( 13 , brbi , uvbi ) ;
VTX_WRITE ( 14 , ImVec2 ( c . x , br . y - stroke_width ) , uvi_cardinal ) ;
VTX_WRITE ( 14 , bbi , uvi_cardinal ) ;
VTX_WRITE ( 15 , blbi , uvbi ) ;
}
@ -2030,7 +2066,7 @@ void ImDrawList::AddCircle(const ImVec2& center, float radius, ImU32 col, int nu
return ;
// First try the fast texture-based renderer, and only if that can't handle this fall back to paths
if ( AddRoundCornerCircle ( this , center , radius , col , false ) )
if ( AddRoundCornerCircle ( this , center , radius , thickness , col , false ) )
return ;
// Obtain segment count
@ -2059,7 +2095,7 @@ void ImDrawList::AddCircleFilled(const ImVec2& center, float radius, ImU32 col,
return ;
// First try the fast texture-based renderer, and only if that can't handle this fall back to paths
if ( AddRoundCornerCircle ( this , center , radius , col , true ) )
if ( AddRoundCornerCircle ( this , center , radius , 1.0f , col , true ) )
return ;
if ( num_segments < = 0 )
@ -3786,17 +3822,47 @@ static void ImFontAtlasBuildRegisterRoundCornersCustomRects(ImFontAtlas* atlas)
return ;
const int pad = FONT_ATLAS_ROUNDED_CORNER_TEX_PADDING ;
const unsigned int max = ImFontAtlasRoundCornersMaxSize ;
const unsigned int max_radius = ImFontAtlasRoundCornersMaxSize ;
const unsigned int max_thickness = ImFontAtlasRoundCornersMaxStrokeWidth ;
atlas - > TexRoundCornerData . reserve ( max ) ;
atlas - > TexRoundCornerData . reserve ( max_radius * max_thickness ) ;
for ( unsigned int n = 0 ; n < max ; n + + )
for ( unsigned int radius_i ndex = 0 ; radius_i ndex < max_radius ; radius_i ndex + + )
{
const int width = n + 1 + pad * 2 ;
const int height = n + 1 + FONT_ATLAS_ROUNDED_CORNER_TEX_CENTER_PADDING + pad * 2 ;
ImFontRoundedCornerData corner_data ;
corner_data . RectId = atlas - > AddCustomRectRegular ( width , height ) ;
atlas - > TexRoundCornerData . push_back ( corner_data ) ;
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 + + )
{
//const unsigned int index = stroke_width_index + (radius_index * ImFontAtlasRoundCornersMaxStrokeWidth);
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 ) )
{
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
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 ) ;
}
}
}
@ -3809,85 +3875,141 @@ static void ImFontAtlasBuildRenderRoundCornersTexData(ImFontAtlas* atlas)
// Render the texture
const int w = atlas - > TexWidth ;
const unsigned int max = ImFontAtlasRoundCornersMaxSize ;
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
for ( unsigned int n = 0 ; n < max ; n + + )
{
const unsigned int id = n ;
ImFontRoundedCornerData & data = atlas - > TexRoundCornerData [ id ] ;
ImFontAtlasCustomRect & r = atlas - > CustomRects [ data . RectId ] ;
IM_ASSERT ( r . IsPacked ( ) ) ;
IM_ASSERT ( r . Width = = n + 1 + pad * 2 & & r . Height = = n + 1 + FONT_ATLAS_ROUNDED_CORNER_TEX_CENTER_PADDING + pad * 2 ) ;
// 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.
const int radius = ( int ) ( r . Width - pad * 2 ) ;
const float stroke_width = 1.0f ;
// Pre-calcuate the parameteric stroke width
data . ParametricStrokeWidth = stroke_width / ( float ) radius ;
for ( int y = - pad ; y < ( int ) ( radius + FONT_ATLAS_ROUNDED_CORNER_TEX_CENTER_PADDING ) ; y + + )
for ( int x = - pad ; x < ( int ) ( radius ) ; x + + )
const unsigned int max_radius = ImFontAtlasRoundCornersMaxSize ;
const unsigned int max_thickness = ImFontAtlasRoundCornersMaxStrokeWidth ;
atlas - > TexRoundCornerData . reserve ( max_radius * max_thickness ) ;
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 ;
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 )
{
// 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.
const bool filled = x < ( y - ( FONT_ATLAS_ROUNDED_CORNER_TEX_CENTER_PADDING > > 1 ) ) ;
if ( filled )
// 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 )
{
// 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 = & atlas - > TexRoundCornerData [ candidate_index ] ;
const float dist = ImSqrt ( ( float ) ( cx * cx + cy * cy ) ) - ( float ) ( radius - ( filled ? 0 : stroke_width ) ) ;
float alpha = 0.0f ;
if ( filled )
{
alpha = ImClamp ( - dist , 0.0f , 1.0f ) ;
if ( candidate_data - > RectId = = data . RectId )
{
other_data = candidate_data ;
other_stroke_width = ( float ) stroke_width_index + 1 ;
other_data - > ParametricStrokeWidth = ( ( other_stroke_width > 1.0f ) ? ( other_stroke_width + 2.0f ) : other_stroke_width ) / ( float ) radius ;
break ;
}
stroke_width_index + + ;
}
else
}
// 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 + + )
{
const float alpha1 = ImClamp ( dist + stroke_width , 0.0f , 1.0f ) ;
const float alpha2 = ImClamp ( dist , 0.0f , 1.0f ) ;
alpha = alpha1 - alpha2 ;
}
// 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 unsigned int offset = ( int ) ( r . X + pad + x ) + ( int ) ( r . Y + pad + y ) * w ;
atlas - > TexPixelsAlpha8 [ offset ] = ( unsigned char ) ( 0xFF * ImSaturate ( alpha ) ) ;
}
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 ( 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 = 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
{
const float alpha1 = ImClamp ( dist + stroke_width , 0.0f , 1.0f ) ;
const float alpha2 = ImClamp ( dist , 0.0f , 1.0f ) ;
alpha = alpha1 - alpha2 ;
}
// 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 unsigned int offset = ( int ) ( r . X + pad + x ) + ( int ) ( r . Y + pad + y ) * w ;
atlas - > TexPixelsAlpha8 [ offset ] = ( unsigned char ) ( 0xFF * ImSaturate ( alpha ) ) ;
}
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 ;
// 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 ;
ImVec2 uv0 , uv1 ;
atlas - > CalcCustomRectUV ( & stage_rect , & uv0 , & uv1 ) ;
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 ;
if ( stage = = 0 )
data . TexUvFilled = ImVec4 ( uv0 . x , uv0 . y , uv1 . x , uv1 . y ) ;
else
data . TexUvStroked = ImVec4 ( uv0 . x , uv0 . y , uv1 . x , uv1 . y ) ;
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.