@ -1,4 +1,4 @@
// dear imgui, v1. 88
// dear imgui, v1. 90.1
// (drawing and font code)
/*
@ -26,38 +26,24 @@ Index of this file:
# define _CRT_SECURE_NO_WARNINGS
# endif
# include "imgui.h"
# ifndef IMGUI_DISABLE
# ifndef IMGUI_DEFINE_MATH_OPERATORS
# define IMGUI_DEFINE_MATH_OPERATORS
# endif
# include "imgui.h"
# ifndef IMGUI_DISABLE
# include "imgui_internal.h"
# ifdef IMGUI_ENABLE_FREETYPE
# include "misc/freetype/imgui_freetype.h"
# endif
# include <stdio.h> // vsnprintf, sscanf, printf
# if !defined(alloca)
# if defined(__GLIBC__) || defined(__sun) || defined(__APPLE__) || defined(__NEWLIB__)
# include <alloca.h> // alloca (glibc uses <alloca.h>. Note that Cygwin may have _WIN32 defined, so the order matters here)
# elif defined(_WIN32)
# include <malloc.h> // alloca
# if !defined(alloca)
# define alloca _alloca // for clang with MS Codegen
# endif
# else
# include <stdlib.h> // alloca
# endif
# endif
// Visual Studio warnings
# ifdef _MSC_VER
# pragma warning (disable: 4127) // condition expression is constant
# pragma warning (disable: 4505) // unreferenced local function has been removed (stb stuff)
# pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen
# pragma warning (disable: 6255) // [Static Analyzer] _alloca indicates failure by raising a stack overflow exception. Consider using _malloca instead.
# pragma warning (disable: 26451) // [Static Analyzer] Arithmetic overflow : Using operator 'xxx' on a 4 byte value and then casting the result to a 8 byte value. Cast the value to the wider type before calling operator 'xxx' to avoid overflow(io.2).
# pragma warning (disable: 26812) // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3). [MSVC Static Analyzer)
# endif
@ -67,9 +53,6 @@ Index of this file:
# if __has_warning("-Wunknown-warning-option")
# pragma clang diagnostic ignored "-Wunknown-warning-option" // warning: unknown warning group 'xxx' // not all warnings are known by all Clang versions and they tend to be rename-happy.. so ignoring warnings triggers new warnings on some configuration. Great!
# endif
# if __has_warning("-Walloca")
# pragma clang diagnostic ignored "-Walloca" // warning: use of function '__builtin_alloca' is discouraged
# endif
# pragma clang diagnostic ignored "-Wunknown-pragmas" // warning: unknown warning group 'xxx'
# pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast // yes, they are more terse.
# pragma clang diagnostic ignored "-Wfloat-equal" // warning: comparing floating point with == or != is unsafe // storing and comparing against same constants ok.
@ -80,6 +63,7 @@ Index of this file:
# pragma clang diagnostic ignored "-Wreserved-id-macro" // warning: macro name is a reserved identifier
# pragma clang diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function // using printf() is a misery with this as C++ va_arg ellipsis changes float to double.
# pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision
# pragma clang diagnostic ignored "-Wreserved-identifier" // warning: identifier '_Xxx' is reserved because it starts with '_' followed by a capital letter
# elif defined(__GNUC__)
# pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind
# pragma GCC diagnostic ignored "-Wunused-function" // warning: 'xxxx' defined but not used
@ -151,7 +135,7 @@ namespace IMGUI_STB_NAMESPACE
# define STBTT_sqrt(x) ImSqrt(x)
# define STBTT_pow(x,y) ImPow(x,y)
# define STBTT_fabs(x) ImFabs(x)
# define STBTT_ifloor(x) ((int)ImFloor Signed (x))
# define STBTT_ifloor(x) ((int)ImFloor (x))
# define STBTT_iceil(x) ((int)ImCeil(x))
# define STBTT_STATIC
# define STB_TRUETYPE_IMPLEMENTATION
@ -402,9 +386,11 @@ void ImDrawListSharedData::SetCircleTessellationMaxError(float max_error)
void ImDrawList : : _ResetForNewFrame ( )
{
// Verify that the ImDrawCmd fields we want to memcmp() are contiguous in memory.
IM_STATIC_ASSERT ( IM_OFFSETOF ( ImDrawCmd , ClipRect ) = = 0 ) ;
IM_STATIC_ASSERT ( IM_OFFSETOF ( ImDrawCmd , TextureId ) = = sizeof ( ImVec4 ) ) ;
IM_STATIC_ASSERT ( IM_OFFSETOF ( ImDrawCmd , VtxOffset ) = = sizeof ( ImVec4 ) + sizeof ( ImTextureID ) ) ;
IM_STATIC_ASSERT ( offsetof ( ImDrawCmd , ClipRect ) = = 0 ) ;
IM_STATIC_ASSERT ( offsetof ( ImDrawCmd , TextureId ) = = sizeof ( ImVec4 ) ) ;
IM_STATIC_ASSERT ( offsetof ( ImDrawCmd , VtxOffset ) = = sizeof ( ImVec4 ) + sizeof ( ImTextureID ) ) ;
if ( _Splitter . _Count > 1 )
_Splitter . Merge ( this ) ;
CmdBuffer . resize ( 0 ) ;
IdxBuffer . resize ( 0 ) ;
@ -463,11 +449,13 @@ void ImDrawList::AddDrawCmd()
// Note that this leaves the ImDrawList in a state unfit for further commands, as most code assume that CmdBuffer.Size > 0 && CmdBuffer.back().UserCallback == NULL
void ImDrawList : : _PopUnusedDrawCmd ( )
{
if ( CmdBuffer . Size = = 0 )
return ;
ImDrawCmd * curr_cmd = & CmdBuffer . Data [ CmdBuffer . Size - 1 ] ;
if ( curr_cmd - > ElemCount = = 0 & & curr_cmd - > UserCallback = = NULL )
while ( CmdBuffer . Size > 0 )
{
ImDrawCmd * curr_cmd = & CmdBuffer . Data [ CmdBuffer . Size - 1 ] ;
if ( curr_cmd - > ElemCount ! = 0 | | curr_cmd - > UserCallback ! = NULL )
return ; // break;
CmdBuffer . pop_back ( ) ;
}
}
void ImDrawList : : AddCallback ( ImDrawCallback callback , void * callback_data )
@ -487,7 +475,7 @@ void ImDrawList::AddCallback(ImDrawCallback callback, void* callback_data)
}
// Compare ClipRect, TextureId and VtxOffset with a single memcmp()
# define ImDrawCmd_HeaderSize ( IM_OFFSETOF (ImDrawCmd, VtxOffset) + sizeof(unsigned int))
# define ImDrawCmd_HeaderSize ( offsetof (ImDrawCmd, VtxOffset) + sizeof(unsigned int))
# define ImDrawCmd_HeaderCompare(CMD_LHS, CMD_RHS) (memcmp(CMD_LHS, CMD_RHS, ImDrawCmd_HeaderSize)) // Compare ClipRect, TextureId, VtxOffset
# define ImDrawCmd_HeaderCopy(CMD_DST, CMD_SRC) (memcpy(CMD_DST, CMD_SRC, ImDrawCmd_HeaderSize)) // Copy ClipRect, TextureId, VtxOffset
# define ImDrawCmd_AreSequentialIdxOffset(CMD_0, CMD_1) (CMD_0->IdxOffset + CMD_0->ElemCount == CMD_1->IdxOffset)
@ -573,7 +561,7 @@ int ImDrawList::_CalcCircleAutoSegmentCount(float radius) const
{
// Automatic segment count
const int radius_idx = ( int ) ( radius + 0.999999f ) ; // ceil to never reduce accuracy
if ( radius_idx < IM_ARRAYSIZE ( _Data - > CircleSegmentCounts ) )
if ( radius_idx >= 0 & & radius_idx < IM_ARRAYSIZE ( _Data - > CircleSegmentCounts ) )
return _Data - > CircleSegmentCounts [ radius_idx ] ; // Use cached value
else
return IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC ( radius , _Data - > CircleSegmentMaxError ) ;
@ -720,7 +708,7 @@ void ImDrawList::PrimQuadUV(const ImVec2& a, const ImVec2& b, const ImVec2& c, c
// We avoid using the ImVec2 math operators here to reduce cost to a minimum for debug/non-inlined builds.
void ImDrawList : : AddPolyline ( const ImVec2 * points , const int points_count , ImU32 col , ImDrawFlags flags , float thickness )
{
if ( points_count < 2 )
if ( points_count < 2 | | ( col & IM_COL32_A_MASK ) = = 0 )
return ;
const bool closed = ( flags & ImDrawFlags_Closed ) ! = 0 ;
@ -753,7 +741,8 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32
// Temporary buffer
// The first <points_count> items are normals at each line point, then after that there are either 2 or 4 temp points for each line point
ImVec2 * temp_normals = ( ImVec2 * ) alloca ( points_count * ( ( use_texture | | ! thick_line ) ? 3 : 5 ) * sizeof ( ImVec2 ) ) ; //-V630
_Data - > TempBuffer . reserve_discard ( points_count * ( ( use_texture | | ! thick_line ) ? 3 : 5 ) ) ;
ImVec2 * temp_normals = _Data - > TempBuffer . Data ;
ImVec2 * temp_points = temp_normals + points_count ;
// Calculate normals (tangents) for each line segment
@ -977,7 +966,7 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32
// - Filled shapes must always use clockwise winding order. The anti-aliasing fringe depends on it. Counter-clockwise shapes will have "inward" anti-aliasing.
void ImDrawList : : AddConvexPolyFilled ( const ImVec2 * points , const int points_count , ImU32 col )
{
if ( points_count < 3 )
if ( points_count < 3 | | ( col & IM_COL32_A_MASK ) = = 0 )
return ;
const ImVec2 uv = _Data - > TexUvWhitePixel ;
@ -1001,7 +990,8 @@ void ImDrawList::AddConvexPolyFilled(const ImVec2* points, const int points_coun
}
// Compute normals
ImVec2 * temp_normals = ( ImVec2 * ) alloca ( points_count * sizeof ( ImVec2 ) ) ; //-V630
_Data - > TempBuffer . reserve_discard ( points_count ) ;
ImVec2 * temp_normals = _Data - > TempBuffer . Data ;
for ( int i0 = points_count - 1 , i1 = 0 ; i1 < points_count ; i0 = i1 + + )
{
const ImVec2 & p0 = points [ i0 ] ;
@ -1201,8 +1191,8 @@ void ImDrawList::PathArcTo(const ImVec2& center, float radius, float a_min, floa
const float a_min_sample_f = IM_DRAWLIST_ARCFAST_SAMPLE_MAX * a_min / ( IM_PI * 2.0f ) ;
const float a_max_sample_f = IM_DRAWLIST_ARCFAST_SAMPLE_MAX * a_max / ( IM_PI * 2.0f ) ;
const int a_min_sample = a_is_reverse ? ( int ) ImFloor Signed ( a_min_sample_f ) : ( int ) ImCeil ( a_min_sample_f ) ;
const int a_max_sample = a_is_reverse ? ( int ) ImCeil ( a_max_sample_f ) : ( int ) ImFloor Signed ( a_max_sample_f ) ;
const int a_min_sample = a_is_reverse ? ( int ) ImFloor ( a_min_sample_f ) : ( int ) ImCeil ( a_min_sample_f ) ;
const int a_max_sample = a_is_reverse ? ( int ) ImCeil ( a_max_sample_f ) : ( int ) ImFloor ( a_max_sample_f ) ;
const int a_mid_samples = a_is_reverse ? ImMax ( a_min_sample - a_max_sample , 0 ) : ImMax ( a_max_sample - a_min_sample , 0 ) ;
const float a_min_segment_angle = a_min_sample * IM_PI * 2.0f / IM_DRAWLIST_ARCFAST_SAMPLE_MAX ;
@ -1227,6 +1217,27 @@ void ImDrawList::PathArcTo(const ImVec2& center, float radius, float a_min, floa
}
}
void ImDrawList : : PathEllipticalArcTo ( const ImVec2 & center , float radius_x , float radius_y , float rot , float a_min , float a_max , int num_segments )
{
if ( num_segments < = 0 )
num_segments = _CalcCircleAutoSegmentCount ( ImMax ( radius_x , radius_y ) ) ; // A bit pessimistic, maybe there's a better computation to do here.
_Path . reserve ( _Path . Size + ( num_segments + 1 ) ) ;
const float cos_rot = ImCos ( rot ) ;
const float sin_rot = ImSin ( rot ) ;
for ( int i = 0 ; i < = num_segments ; i + + )
{
const float a = a_min + ( ( float ) i / ( float ) num_segments ) * ( a_max - a_min ) ;
ImVec2 point ( ImCos ( a ) * radius_x , ImSin ( a ) * radius_y ) ;
const float rel_x = ( point . x * cos_rot ) - ( point . y * sin_rot ) ;
const float rel_y = ( point . x * sin_rot ) + ( point . y * cos_rot ) ;
point . x = rel_x + center . x ;
point . y = rel_y + center . y ;
_Path . push_back ( point ) ;
}
}
ImVec2 ImBezierCubicCalc ( const ImVec2 & p1 , const ImVec2 & p2 , const ImVec2 & p3 , const ImVec2 & p4 , float t )
{
float u = 1.0f - t ;
@ -1295,6 +1306,7 @@ void ImDrawList::PathBezierCubicCurveTo(const ImVec2& p2, const ImVec2& p3, cons
ImVec2 p1 = _Path . back ( ) ;
if ( num_segments = = 0 )
{
IM_ASSERT ( _Data - > CurveTessellationTol > 0.0f ) ;
PathBezierCubicCurveToCasteljau ( & _Path , p1 . x , p1 . y , p2 . x , p2 . y , p3 . x , p3 . y , p4 . x , p4 . y , _Data - > CurveTessellationTol , 0 ) ; // Auto-tessellated
}
else
@ -1310,6 +1322,7 @@ void ImDrawList::PathBezierQuadraticCurveTo(const ImVec2& p2, const ImVec2& p3,
ImVec2 p1 = _Path . back ( ) ;
if ( num_segments = = 0 )
{
IM_ASSERT ( _Data - > CurveTessellationTol > 0.0f ) ;
PathBezierQuadraticCurveToCasteljau ( & _Path , p1 . x , p1 . y , p2 . x , p2 . y , p3 . x , p3 . y , _Data - > CurveTessellationTol , 0 ) ; // Auto-tessellated
}
else
@ -1320,32 +1333,22 @@ void ImDrawList::PathBezierQuadraticCurveTo(const ImVec2& p2, const ImVec2& p3,
}
}
IM_STATIC_ASSERT ( ImDrawFlags_RoundCornersTopLeft = = ( 1 < < 4 ) ) ;
static inline ImDrawFlags FixRectCornerFlags ( ImDrawFlags flags )
{
/*
IM_STATIC_ASSERT ( ImDrawFlags_RoundCornersTopLeft = = ( 1 < < 4 ) ) ;
# ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
// Legacy Support for hard coded ~0 (used to be a suggested equivalent to ImDrawCornerFlags_All)
// ~0 --> ImDrawFlags_RoundCornersAll or 0
if ( flags = = ~ 0 )
return ImDrawFlags_RoundCornersAll ;
// Legacy Support for hard coded 0x01 to 0x0F (matching 15 out of 16 old flags combinations)
// 0x01 --> ImDrawFlags_RoundCornersTopLeft (VALUE 0x01 OVERLAPS ImDrawFlags_Closed but ImDrawFlags_Closed is never valid in this path!)
// 0x02 --> ImDrawFlags_RoundCornersTopRight
// 0x03 --> ImDrawFlags_RoundCornersTopLeft | ImDrawFlags_RoundCornersTopRight
// 0x04 --> ImDrawFlags_RoundCornersBotLeft
// 0x05 --> ImDrawFlags_RoundCornersTopLeft | ImDrawFlags_RoundCornersBotLeft
// ...
// 0x0F --> ImDrawFlags_RoundCornersAll or 0
// (See all values in ImDrawCornerFlags_)
if ( flags > = 0x01 & & flags < = 0x0F )
return ( flags < < 4 ) ;
// Obsoleted in 1.82 (from February 2021). This code was stripped/simplified and mostly commented in 1.90 (from September 2023)
// - Legacy Support for hard coded ~0 (used to be a suggested equivalent to ImDrawCornerFlags_All)
if ( flags = = ~ 0 ) { return ImDrawFlags_RoundCornersAll ; }
// - Legacy Support for hard coded 0x01 to 0x0F (matching 15 out of 16 old flags combinations). Read details in older version of this code.
if ( flags > = 0x01 & & flags < = 0x0F ) { return ( flags < < 4 ) ; }
// We cannot support hard coded 0x00 with 'float rounding > 0.0f' --> replace with ImDrawFlags_RoundCornersNone or use 'float rounding = 0.0f'
# endif
// If this triggers, please update your code replacing hardcoded values with new ImDrawFlags_RoundCorners* values.
// Note that ImDrawFlags_Closed (== 0x01) is an invalid flag for AddRect(), AddRectFilled(), PathRect() etc...
*/
// If this assert triggers, please update your code replacing hardcoded values with new ImDrawFlags_RoundCorners* values.
// Note that ImDrawFlags_Closed (== 0x01) is an invalid flag for AddRect(), AddRectFilled(), PathRect() etc. anyway.
// See details in 1.82 Changelog as well as 2021/03/12 and 2023/09/08 entries in "API BREAKING CHANGES" section.
IM_ASSERT ( ( flags & 0x0F ) = = 0 & & " Misuse of legacy hardcoded ImDrawCornerFlags values! " ) ;
if ( ( flags & ImDrawFlags_RoundCornersMask_ ) = = 0 )
@ -1356,10 +1359,12 @@ static inline ImDrawFlags FixRectCornerFlags(ImDrawFlags flags)
void ImDrawList : : PathRect ( const ImVec2 & a , const ImVec2 & b , float rounding , ImDrawFlags flags )
{
flags = FixRectCornerFlags ( flags ) ;
rounding = ImMin ( rounding , ImFabs ( b . x - a . x ) * ( ( ( flags & ImDrawFlags_RoundCornersTop ) = = ImDrawFlags_RoundCornersTop ) | | ( ( flags & ImDrawFlags_RoundCornersBottom ) = = ImDrawFlags_RoundCornersBottom ) ? 0.5f : 1.0f ) - 1.0f ) ;
rounding = ImMin ( rounding , ImFabs ( b . y - a . y ) * ( ( ( flags & ImDrawFlags_RoundCornersLeft ) = = ImDrawFlags_RoundCornersLeft ) | | ( ( flags & ImDrawFlags_RoundCornersRight ) = = ImDrawFlags_RoundCornersRight ) ? 0.5f : 1.0f ) - 1.0f ) ;
if ( rounding > = 0.5f )
{
flags = FixRectCornerFlags ( flags ) ;
rounding = ImMin ( rounding , ImFabs ( b . x - a . x ) * ( ( ( flags & ImDrawFlags_RoundCornersTop ) = = ImDrawFlags_RoundCornersTop ) | | ( ( flags & ImDrawFlags_RoundCornersBottom ) = = ImDrawFlags_RoundCornersBottom ) ? 0.5f : 1.0f ) - 1.0f ) ;
rounding = ImMin ( rounding , ImFabs ( b . y - a . y ) * ( ( ( flags & ImDrawFlags_RoundCornersLeft ) = = ImDrawFlags_RoundCornersLeft ) | | ( ( flags & ImDrawFlags_RoundCornersRight ) = = ImDrawFlags_RoundCornersRight ) ? 0.5f : 1.0f ) - 1.0f ) ;
}
if ( rounding < 0.5f | | ( flags & ImDrawFlags_RoundCornersMask_ ) = = ImDrawFlags_RoundCornersNone )
{
PathLineTo ( a ) ;
@ -1552,6 +1557,35 @@ void ImDrawList::AddNgonFilled(const ImVec2& center, float radius, ImU32 col, in
PathFillConvex ( col ) ;
}
// Ellipse
void ImDrawList : : AddEllipse ( const ImVec2 & center , float radius_x , float radius_y , ImU32 col , float rot , int num_segments , float thickness )
{
if ( ( col & IM_COL32_A_MASK ) = = 0 )
return ;
if ( num_segments < = 0 )
num_segments = _CalcCircleAutoSegmentCount ( ImMax ( radius_x , radius_y ) ) ; // A bit pessimistic, maybe there's a better computation to do here.
// Because we are filling a closed shape we remove 1 from the count of segments/points
const float a_max = IM_PI * 2.0f * ( ( float ) num_segments - 1.0f ) / ( float ) num_segments ;
PathEllipticalArcTo ( center , radius_x , radius_y , rot , 0.0f , a_max , num_segments - 1 ) ;
PathStroke ( col , true , thickness ) ;
}
void ImDrawList : : AddEllipseFilled ( const ImVec2 & center , float radius_x , float radius_y , ImU32 col , float rot , int num_segments )
{
if ( ( col & IM_COL32_A_MASK ) = = 0 )
return ;
if ( num_segments < = 0 )
num_segments = _CalcCircleAutoSegmentCount ( ImMax ( radius_x , radius_y ) ) ; // A bit pessimistic, maybe there's a better computation to do here.
// Because we are filling a closed shape we remove 1 from the count of segments/points
const float a_max = IM_PI * 2.0f * ( ( float ) num_segments - 1.0f ) / ( float ) num_segments ;
PathEllipticalArcTo ( center , radius_x , radius_y , rot , 0.0f , a_max , num_segments - 1 ) ;
PathFillConvex ( col ) ;
}
// Cubic Bezier takes 4 controls points
void ImDrawList : : AddBezierCubic ( const ImVec2 & p1 , const ImVec2 & p2 , const ImVec2 & p3 , const ImVec2 & p4 , ImU32 col , float thickness , int num_segments )
{
@ -1816,6 +1850,63 @@ void ImDrawListSplitter::SetCurrentChannel(ImDrawList* draw_list, int idx)
// [SECTION] ImDrawData
//-----------------------------------------------------------------------------
void ImDrawData : : Clear ( )
{
Valid = false ;
CmdListsCount = TotalIdxCount = TotalVtxCount = 0 ;
CmdLists . resize ( 0 ) ; // The ImDrawList are NOT owned by ImDrawData but e.g. by ImGuiContext, so we don't clear them.
DisplayPos = DisplaySize = FramebufferScale = ImVec2 ( 0.0f , 0.0f ) ;
OwnerViewport = NULL ;
}
// Important: 'out_list' is generally going to be draw_data->CmdLists, but may be another temporary list
// as long at it is expected that the result will be later merged into draw_data->CmdLists[].
void ImGui : : AddDrawListToDrawDataEx ( ImDrawData * draw_data , ImVector < ImDrawList * > * out_list , ImDrawList * draw_list )
{
if ( draw_list - > CmdBuffer . Size = = 0 )
return ;
if ( draw_list - > CmdBuffer . Size = = 1 & & draw_list - > CmdBuffer [ 0 ] . ElemCount = = 0 & & draw_list - > CmdBuffer [ 0 ] . UserCallback = = NULL )
return ;
// Draw list sanity check. Detect mismatch between PrimReserve() calls and incrementing _VtxCurrentIdx, _VtxWritePtr etc.
// May trigger for you if you are using PrimXXX functions incorrectly.
IM_ASSERT ( draw_list - > VtxBuffer . Size = = 0 | | draw_list - > _VtxWritePtr = = draw_list - > VtxBuffer . Data + draw_list - > VtxBuffer . Size ) ;
IM_ASSERT ( draw_list - > IdxBuffer . Size = = 0 | | draw_list - > _IdxWritePtr = = draw_list - > IdxBuffer . Data + draw_list - > IdxBuffer . Size ) ;
if ( ! ( draw_list - > Flags & ImDrawListFlags_AllowVtxOffset ) )
IM_ASSERT ( ( int ) draw_list - > _VtxCurrentIdx = = draw_list - > VtxBuffer . Size ) ;
// Check that draw_list doesn't use more vertices than indexable (default ImDrawIdx = unsigned short = 2 bytes = 64K vertices per ImDrawList = per window)
// If this assert triggers because you are drawing lots of stuff manually:
// - First, make sure you are coarse clipping yourself and not trying to draw many things outside visible bounds.
// Be mindful that the lower-level ImDrawList API doesn't filter vertices. Use the Metrics/Debugger window to inspect draw list contents.
// - If you want large meshes with more than 64K vertices, you can either:
// (A) Handle the ImDrawCmd::VtxOffset value in your renderer backend, and set 'io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset'.
// Most example backends already support this from 1.71. Pre-1.71 backends won't.
// Some graphics API such as GL ES 1/2 don't have a way to offset the starting vertex so it is not supported for them.
// (B) Or handle 32-bit indices in your renderer backend, and uncomment '#define ImDrawIdx unsigned int' line in imconfig.h.
// Most example backends already support this. For example, the OpenGL example code detect index size at compile-time:
// glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer_offset);
// Your own engine or render API may use different parameters or function calls to specify index sizes.
// 2 and 4 bytes indices are generally supported by most graphics API.
// - If for some reason neither of those solutions works for you, a workaround is to call BeginChild()/EndChild() before reaching
// the 64K limit to split your draw commands in multiple draw lists.
if ( sizeof ( ImDrawIdx ) = = 2 )
IM_ASSERT ( draw_list - > _VtxCurrentIdx < ( 1 < < 16 ) & & " Too many vertices in ImDrawList using 16-bit indices. Read comment above " ) ;
// Add to output list + records state in ImDrawData
out_list - > push_back ( draw_list ) ;
draw_data - > CmdListsCount + + ;
draw_data - > TotalVtxCount + = draw_list - > VtxBuffer . Size ;
draw_data - > TotalIdxCount + = draw_list - > IdxBuffer . Size ;
}
void ImDrawData : : AddDrawList ( ImDrawList * draw_list )
{
IM_ASSERT ( CmdLists . Size = = CmdListsCount ) ;
draw_list - > _PopUnusedDrawCmd ( ) ;
ImGui : : AddDrawListToDrawDataEx ( this , & CmdLists , draw_list ) ;
}
// For backward compatibility: convert all buffers from indexed to de-indexed, in case you cannot render indexed. Note: this is slow and most likely a waste of resources. Always prefer indexed rendering!
void ImDrawData : : DeIndexAllBuffers ( )
{
@ -1840,15 +1931,9 @@ void ImDrawData::DeIndexAllBuffers()
// or if there is a difference between your window resolution and framebuffer resolution.
void ImDrawData : : ScaleClipRects ( const ImVec2 & fb_scale )
{
for ( int i = 0 ; i < CmdListsCount ; i + + )
{
ImDrawList * cmd_list = CmdLists [ i ] ;
for ( int cmd_i = 0 ; cmd_i < cmd_list - > CmdBuffer . Size ; cmd_i + + )
{
ImDrawCmd * cmd = & cmd_list - > CmdBuffer [ cmd_i ] ;
cmd - > ClipRect = ImVec4 ( cmd - > ClipRect . x * fb_scale . x , cmd - > ClipRect . y * fb_scale . y , cmd - > ClipRect . z * fb_scale . x , cmd - > ClipRect . w * fb_scale . y ) ;
}
}
for ( ImDrawList * draw_list : CmdLists )
for ( ImDrawCmd & cmd : draw_list - > CmdBuffer )
cmd . ClipRect = ImVec4 ( cmd . ClipRect . x * fb_scale . x , cmd . ClipRect . y * fb_scale . y , cmd . ClipRect . z * fb_scale . x , cmd . ClipRect . w * fb_scale . y ) ;
}
//-----------------------------------------------------------------------------
@ -1904,6 +1989,14 @@ void ImGui::ShadeVertsLinearUV(ImDrawList* draw_list, int vert_start_idx, int ve
}
}
void ImGui : : ShadeVertsTransformPos ( ImDrawList * draw_list , int vert_start_idx , int vert_end_idx , const ImVec2 & pivot_in , float cos_a , float sin_a , const ImVec2 & pivot_out )
{
ImDrawVert * vert_start = draw_list - > VtxBuffer . Data + vert_start_idx ;
ImDrawVert * vert_end = draw_list - > VtxBuffer . Data + vert_end_idx ;
for ( ImDrawVert * vertex = vert_start ; vertex < vert_end ; + + vertex )
vertex - > pos = ImRotate ( vertex - > pos - pivot_in , cos_a , sin_a ) + pivot_out ;
}
//-----------------------------------------------------------------------------
// [SECTION] ImFontConfig
//-----------------------------------------------------------------------------
@ -1912,10 +2005,11 @@ ImFontConfig::ImFontConfig()
{
memset ( this , 0 , sizeof ( * this ) ) ;
FontDataOwnedByAtlas = true ;
OversampleH = 3; // FIXME: 2 may be a better default?
OversampleH = 2;
OversampleV = 1 ;
GlyphMaxAdvanceX = FLT_MAX ;
RasterizerMultiply = 1.0f ;
RasterizerDensity = 1.0f ;
EllipsisChar = ( ImWchar ) - 1 ;
}
@ -1989,19 +2083,19 @@ ImFontAtlas::~ImFontAtlas()
void ImFontAtlas : : ClearInputData ( )
{
IM_ASSERT ( ! Locked & & " Cannot modify a locked ImFontAtlas between NewFrame() and EndFrame/Render()! " ) ;
for ( int i = 0 ; i < ConfigData . Size ; i + + )
if ( ConfigData[ i ] . FontData & & ConfigData [ i ] . FontDataOwnedByAtlas )
for ( ImFontConfig & font_cfg : ConfigData )
if ( font_cfg. FontData & & font_cfg . FontDataOwnedByAtlas )
{
IM_FREE ( ConfigData[ i ] . FontData ) ;
ConfigData[ i ] . FontData = NULL ;
IM_FREE ( font_cfg . FontData ) ;
font_cfg . FontData = NULL ;
}
// When clearing this we lose access to the font name and other information used to build the font.
for ( int i = 0 ; i < Fonts . Size ; i + + )
if ( Fonts[ i ] - > ConfigData > = ConfigData . Data & & Fonts[ i ] - > ConfigData < ConfigData . Data + ConfigData . Size )
for ( ImFont * font : Fonts )
if ( font - > ConfigData > = ConfigData . Data & & font - > ConfigData < ConfigData . Data + ConfigData . Size )
{
Fonts[ i ] - > ConfigData = NULL ;
Fonts[ i ] - > ConfigDataCount = 0 ;
font - > ConfigData = NULL ;
font - > ConfigDataCount = 0 ;
}
ConfigData . clear ( ) ;
CustomRects . clear ( ) ;
@ -2098,6 +2192,8 @@ ImFont* ImFontAtlas::AddFont(const ImFontConfig* font_cfg)
if ( new_font_cfg . DstFont - > EllipsisChar = = ( ImWchar ) - 1 )
new_font_cfg . DstFont - > EllipsisChar = font_cfg - > EllipsisChar ;
ImFontAtlasUpdateConfigDataPointers ( this ) ;
// Invalidate texture
TexReady = false ;
ClearTexData ( ) ;
@ -2134,7 +2230,7 @@ ImFont* ImFontAtlas::AddFontDefault(const ImFontConfig* font_cfg_template)
if ( font_cfg . Name [ 0 ] = = ' \0 ' )
ImFormatString ( font_cfg . Name , IM_ARRAYSIZE ( font_cfg . Name ) , " ProggyClean.ttf, %dpx " , ( int ) font_cfg . SizePixels ) ;
font_cfg . EllipsisChar = ( ImWchar ) 0x0085 ;
font_cfg . GlyphOffset . y = 1.0f * IM_ FLOOR ( font_cfg . SizePixels / 13.0f ) ; // Add +1 offset per 13 units
font_cfg . GlyphOffset . y = 1.0f * IM_ TRUNC ( font_cfg . SizePixels / 13.0f ) ; // Add +1 offset per 13 units
const char * ttf_compressed_base85 = GetDefaultCompressedFontDataTTFBase85 ( ) ;
const ImWchar * glyph_ranges = font_cfg . GlyphRanges ! = NULL ? font_cfg . GlyphRanges : GetGlyphRangesDefault ( ) ;
@ -2164,13 +2260,14 @@ ImFont* ImFontAtlas::AddFontFromFileTTF(const char* filename, float size_pixels,
}
// NB: Transfer ownership of 'ttf_data' to ImFontAtlas, unless font_cfg_template->FontDataOwnedByAtlas == false. Owned TTF buffer will be deleted after Build().
ImFont * ImFontAtlas : : AddFontFromMemoryTTF ( void * ttf_data, int ttf _size, float size_pixels , const ImFontConfig * font_cfg_template , const ImWchar * glyph_ranges )
ImFont * ImFontAtlas : : AddFontFromMemoryTTF ( void * font_data, int font_data _size, float size_pixels , const ImFontConfig * font_cfg_template , const ImWchar * glyph_ranges )
{
IM_ASSERT ( ! Locked & & " Cannot modify a locked ImFontAtlas between NewFrame() and EndFrame/Render()! " ) ;
ImFontConfig font_cfg = font_cfg_template ? * font_cfg_template : ImFontConfig ( ) ;
IM_ASSERT ( font_cfg . FontData = = NULL ) ;
font_cfg . FontData = ttf_data ;
font_cfg . FontDataSize = ttf_size ;
IM_ASSERT ( font_data_size > 100 & & " Incorrect value for font_data_size! " ) ; // Heuristic to prevent accidentally passing a wrong value to font_data_size.
font_cfg . FontData = font_data ;
font_cfg . FontDataSize = font_data_size ;
font_cfg . SizePixels = size_pixels > 0.0f ? size_pixels : font_cfg . SizePixels ;
if ( glyph_ranges )
font_cfg . GlyphRanges = glyph_ranges ;
@ -2298,10 +2395,11 @@ void ImFontAtlasBuildMultiplyCalcLookupTable(unsigned char out_table[256], fl
void ImFontAtlasBuildMultiplyRectAlpha8 ( const unsigned char table [ 256 ] , unsigned char * pixels , int x , int y , int w , int h , int stride )
{
IM_ASSERT_PARANOID ( w < = stride ) ;
unsigned char * data = pixels + x + y * stride ;
for ( int j = h ; j > 0 ; j - - , data + = stride )
for ( int i = 0 ; i < w ; i + + )
data [ i ] = table [ data [ i ] ] ;
for ( int j = h ; j > 0 ; j - - , data + = stride - w )
for ( int i = w ; i > 0 ; i - - , data + + )
* data = table [ * data ] ;
}
# ifdef IMGUI_ENABLE_STB_TRUETYPE
@ -2318,7 +2416,7 @@ struct ImFontBuildSrcData
int GlyphsHighest ; // Highest requested codepoint
int GlyphsCount ; // Glyph count (excluding missing glyphs and glyphs already set by an earlier source font)
ImBitVector GlyphsSet ; // Glyph bit map (random access, 1-bit per codepoint. This will be a maximum of 8KB)
ImVector < int > GlyphsList ; // Glyph codepoints list (flattened version of Glyphs Map )
ImVector < int > GlyphsList ; // Glyph codepoints list (flattened version of Glyphs Set )
} ;
// Temporary data for one destination ImFont* (multiple source fonts can be merged into one destination ImFont)
@ -2384,13 +2482,21 @@ static bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas)
const int font_offset = stbtt_GetFontOffsetForIndex ( ( unsigned char * ) cfg . FontData , cfg . FontNo ) ;
IM_ASSERT ( font_offset > = 0 & & " FontData is incorrect, or FontNo cannot be found. " ) ;
if ( ! stbtt_InitFont ( & src_tmp . FontInfo , ( unsigned char * ) cfg . FontData , font_offset ) )
{
IM_ASSERT ( 0 & & " stbtt_InitFont(): failed to parse FontData. It is correct and complete? Check FontDataSize. " ) ;
return false ;
}
// Measure highest codepoints
ImFontBuildDstData & dst_tmp = dst_tmp_array [ src_tmp . DstIndex ] ;
src_tmp . SrcRanges = cfg . GlyphRanges ? cfg . GlyphRanges : atlas - > GetGlyphRangesDefault ( ) ;
for ( const ImWchar * src_range = src_tmp . SrcRanges ; src_range [ 0 ] & & src_range [ 1 ] ; src_range + = 2 )
{
// Check for valid range. This may also help detect *some* dangling pointers, because a common
// user error is to setup ImFontConfig::GlyphRanges with a pointer to data that isn't persistent.
IM_ASSERT ( src_range [ 0 ] < = src_range [ 1 ] ) ;
src_tmp . GlyphsHighest = ImMax ( src_tmp . GlyphsHighest , ( int ) src_range [ 1 ] ) ;
}
dst_tmp . SrcCount + + ;
dst_tmp . GlyphsHighest = ImMax ( dst_tmp . GlyphsHighest , src_tmp . GlyphsHighest ) ;
}
@ -2461,7 +2567,7 @@ static bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas)
// Convert our ranges in the format stb_truetype wants
ImFontConfig & cfg = atlas - > ConfigData [ src_i ] ;
src_tmp . PackRange . font_size = cfg . SizePixels ;
src_tmp . PackRange . font_size = cfg . SizePixels * cfg . RasterizerDensity ;
src_tmp . PackRange . first_unicode_codepoint_in_range = 0 ;
src_tmp . PackRange . array_of_unicode_codepoints = src_tmp . GlyphsList . Data ;
src_tmp . PackRange . num_chars = src_tmp . GlyphsList . Size ;
@ -2470,7 +2576,7 @@ static bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas)
src_tmp . PackRange . v_oversample = ( unsigned char ) cfg . OversampleV ;
// Gather the sizes of all rectangles we will need to pack (this loop is based on stbtt_PackFontRangesGatherRects)
const float scale = ( cfg . SizePixels > 0 ) ? stbtt_ScaleForPixelHeight ( & src_tmp . FontInfo , cfg . SizePixels ) : stbtt_ScaleForMappingEmToPixels ( & src_tmp . FontInfo , - cfg . SizePixels ) ;
const float scale = ( cfg . SizePixels > 0.0f ) ? stbtt_ScaleForPixelHeight ( & src_tmp . FontInfo , cfg . SizePixels * cfg . RasterizerDensity ) : stbtt_ScaleForMappingEmToPixels ( & src_tmp . FontInfo , - cfg . SizePixels * cfg . RasterizerDensity ) ;
const int padding = atlas - > TexGlyphPadding ;
for ( int glyph_i = 0 ; glyph_i < src_tmp . GlyphsList . Size ; glyph_i + + )
{
@ -2555,13 +2661,10 @@ static bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas)
// 9. Setup ImFont and glyphs for runtime
for ( int src_i = 0 ; src_i < src_tmp_array . Size ; src_i + + )
{
ImFontBuildSrcData & src_tmp = src_tmp_array [ src_i ] ;
if ( src_tmp . GlyphsCount = = 0 )
continue ;
// When merging fonts with MergeMode=true:
// - We can have multiple input fonts writing into a same destination font.
// - dst_font->ConfigData is != from cfg which is our source configuration.
ImFontBuildSrcData & src_tmp = src_tmp_array [ src_i ] ;
ImFontConfig & cfg = atlas - > ConfigData [ src_i ] ;
ImFont * dst_font = cfg . DstFont ;
@ -2569,12 +2672,14 @@ static bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas)
int unscaled_ascent , unscaled_descent , unscaled_line_gap ;
stbtt_GetFontVMetrics ( & src_tmp . FontInfo , & unscaled_ascent , & unscaled_descent , & unscaled_line_gap ) ;
const float ascent = Im Floor ( unscaled_ascent * font_scale + ( ( unscaled_ascent > 0.0f ) ? + 1 : - 1 ) ) ;
const float descent = Im Floor ( unscaled_descent * font_scale + ( ( unscaled_descent > 0.0f ) ? + 1 : - 1 ) ) ;
const float ascent = Im Trunc ( unscaled_ascent * font_scale + ( ( unscaled_ascent > 0.0f ) ? + 1 : - 1 ) ) ;
const float descent = Im Trunc ( unscaled_descent * font_scale + ( ( unscaled_descent > 0.0f ) ? + 1 : - 1 ) ) ;
ImFontAtlasBuildSetupFont ( atlas , dst_font , & cfg , ascent , descent ) ;
const float font_off_x = cfg . GlyphOffset . x ;
const float font_off_y = cfg . GlyphOffset . y + IM_ROUND ( dst_font - > Ascent ) ;
const float inv_rasterization_scale = 1.0f / cfg . RasterizerDensity ;
for ( int glyph_i = 0 ; glyph_i < src_tmp . GlyphsCount ; glyph_i + + )
{
// Register glyph
@ -2583,7 +2688,11 @@ static bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas)
stbtt_aligned_quad q ;
float unused_x = 0.0f , unused_y = 0.0f ;
stbtt_GetPackedQuad ( src_tmp . PackedChars , atlas - > TexWidth , atlas - > TexHeight , glyph_i , & unused_x , & unused_y , & q , 0 ) ;
dst_font - > AddGlyph ( & cfg , ( ImWchar ) codepoint , q . x0 + font_off_x , q . y0 + font_off_y , q . x1 + font_off_x , q . y1 + font_off_y , q . s0 , q . t0 , q . s1 , q . t1 , pc . xadvance ) ;
float x0 = q . x0 * inv_rasterization_scale + font_off_x ;
float y0 = q . y0 * inv_rasterization_scale + font_off_y ;
float x1 = q . x1 * inv_rasterization_scale + font_off_x ;
float y1 = q . y1 * inv_rasterization_scale + font_off_y ;
dst_font - > AddGlyph ( & cfg , ( ImWchar ) codepoint , x0 , y0 , x1 , y1 , q . s0 , q . t0 , q . s1 , q . t1 , pc . xadvance * inv_rasterization_scale ) ;
}
}
@ -2603,19 +2712,31 @@ const ImFontBuilderIO* ImFontAtlasGetBuilderForStbTruetype()
# endif // IMGUI_ENABLE_STB_TRUETYPE
void ImFontAtlasUpdateConfigDataPointers ( ImFontAtlas * atlas )
{
for ( ImFontConfig & font_cfg : atlas - > ConfigData )
{
ImFont * font = font_cfg . DstFont ;
if ( ! font_cfg . MergeMode )
{
font - > ConfigData = & font_cfg ;
font - > ConfigDataCount = 0 ;
}
font - > ConfigDataCount + + ;
}
}
void ImFontAtlasBuildSetupFont ( ImFontAtlas * atlas , ImFont * font , ImFontConfig * font_config , float ascent , float descent )
{
if ( ! font_config - > MergeMode )
{
font - > ClearOutputData ( ) ;
font - > FontSize = font_config - > SizePixels ;
font - > ConfigData = font_config ;
font - > ConfigDataCount = 0 ;
IM_ASSERT ( font - > ConfigData = = font_config ) ;
font - > ContainerAtlas = atlas ;
font - > Ascent = ascent ;
font - > Descent = descent ;
}
font - > ConfigDataCount + + ;
}
void ImFontAtlasBuildPackCustomRects ( ImFontAtlas * atlas , void * stbrp_context_opaque )
@ -2625,6 +2746,9 @@ void ImFontAtlasBuildPackCustomRects(ImFontAtlas* atlas, void* stbrp_context_opa
ImVector < ImFontAtlasCustomRect > & user_rects = atlas - > CustomRects ;
IM_ASSERT ( user_rects . Size > = 1 ) ; // We expect at least the default custom rects to be registered, else something went wrong.
# ifdef __GNUC__
if ( user_rects . Size < 1 ) { __builtin_unreachable ( ) ; } // Workaround for GCC bug if IM_ASSERT() is defined to conditionally throw (see #5343)
# endif
ImVector < stbrp_rect > pack_rects ;
pack_rects . resize ( user_rects . Size ) ;
@ -2759,6 +2883,13 @@ static void ImFontAtlasBuildRenderLinesTexData(ImFontAtlas* atlas)
// Note: this is called / shared by both the stb_truetype and the FreeType builder
void ImFontAtlasBuildInit ( ImFontAtlas * atlas )
{
// Round font size
// - We started rounding in 1.90 WIP (18991) as our layout system currently doesn't support non-rounded font size well yet.
// - Note that using io.FontGlobalScale or SetWindowFontScale(), with are legacy-ish, partially supported features, can still lead to unrounded sizes.
// - We may support it better later and remove this rounding.
for ( ImFontConfig & cfg : atlas - > ConfigData )
cfg . SizePixels = ImTrunc ( cfg . SizePixels ) ;
// Register texture region for mouse cursors or standard white pixels
if ( atlas - > PackIdMouseCursors < 0 )
{
@ -2800,9 +2931,9 @@ void ImFontAtlasBuildFinish(ImFontAtlas* atlas)
}
// Build all fonts lookup tables
for ( int i = 0 ; i < atlas - > Fonts . Size ; i + + )
if ( atlas- > Fonts [ i ] - > DirtyLookupTables )
atlas- > Fonts [ i ] - > BuildLookupTable ( ) ;
for ( ImFont * font : atlas - > Fonts )
if ( font - > DirtyLookupTables )
font - > BuildLookupTable ( ) ;
atlas - > TexReady = true ;
}
@ -2818,6 +2949,17 @@ const ImWchar* ImFontAtlas::GetGlyphRangesDefault()
return & ranges [ 0 ] ;
}
const ImWchar * ImFontAtlas : : GetGlyphRangesGreek ( )
{
static const ImWchar ranges [ ] =
{
0x0020 , 0x00FF , // Basic Latin + Latin Supplement
0x0370 , 0x03FF , // Greek and Coptic
0 ,
} ;
return & ranges [ 0 ] ;
}
const ImWchar * ImFontAtlas : : GetGlyphRangesKorean ( )
{
static const ImWchar ranges [ ] =
@ -2934,19 +3076,19 @@ const ImWchar* ImFontAtlas::GetGlyphRangesJapanese()
// 2999 ideograms code points for Japanese
// - 2136 Joyo (meaning "for regular use" or "for common use") Kanji code points
// - 863 Jinmeiyo (meaning "for personal name") Kanji code points
// - Sourced from the character information database of the Information-technology Promotion Agency, Japan
// - https://mojikiban.ipa.go.jp/mji/
// - Available under the terms of the Creative Commons Attribution-ShareAlike 2.1 Japan (CC BY-SA 2.1 JP).
// - https://creativecommons.org/licenses/by-sa/2.1/jp/deed.en
// - https://creativecommons.org/licenses/by-sa/2.1/jp/legalcode
// - You can generate this code by the script at:
// - https://github.com/vaiorabbit/everyday_use_kanji
// - Sourced from official information provided by the government agencies of Japan:
// - List of Joyo Kanji by the Agency for Cultural Affairs
// - https://www.bunka.go.jp/kokugo_nihongo/sisaku/joho/joho/kijun/naikaku/kanji/
// - List of Jinmeiyo Kanji by the Ministry of Justice
// - http://www.moj.go.jp/MINJI/minji86.html
// - Available under the terms of the Creative Commons Attribution 4.0 International (CC BY 4.0).
// - https://creativecommons.org/licenses/by/4.0/legalcode
// - You can generate this code by the script at:
// - https://github.com/vaiorabbit/everyday_use_kanji
// - References:
// - List of Joyo Kanji
// - (Official list by the Agency for Cultural Affairs) https://www.bunka.go.jp/kokugo_nihongo/sisaku/joho/joho/kakuki/14/tosin02/index.html
// - (Wikipedia) https://en.wikipedia.org/wiki/List_of_j%C5%8Dy%C5%8D_kanji
// - List of Jinmeiyo Kanji
// - (Official list by the Ministry of Justice) http://www.moj.go.jp/MINJI/minji86.html
// - (Wikipedia) https://en.wikipedia.org/wiki/Jinmeiy%C5%8D_kanji
// - Missing 1 Joyo Kanji: U+20B9F (Kun'yomi: Shikaru, On'yomi: Shitsu,shichi), see https://github.com/ocornut/imgui/pull/3627 for details.
// You can use ImFontGlyphRangesBuilder to create your own ranges derived from this, by merging existing ranges or adding new characters.
@ -3109,7 +3251,8 @@ ImFont::ImFont()
FallbackAdvanceX = 0.0f ;
FallbackChar = ( ImWchar ) - 1 ;
EllipsisChar = ( ImWchar ) - 1 ;
DotChar = ( ImWchar ) - 1 ;
EllipsisWidth = EllipsisCharStep = 0.0f ;
EllipsisCharCount = 0 ;
FallbackGlyph = NULL ;
ContainerAtlas = NULL ;
ConfigData = NULL ;
@ -3155,6 +3298,7 @@ void ImFont::BuildLookupTable()
max_codepoint = ImMax ( max_codepoint , ( int ) Glyphs [ i ] . Codepoint ) ;
// Build lookup table
IM_ASSERT ( Glyphs . Size > 0 & & " Font has not loaded glyph! " ) ;
IM_ASSERT ( Glyphs . Size < 0xFFFF ) ; // -1 is reserved
IndexAdvanceX . clear ( ) ;
IndexLookup . clear ( ) ;
@ -3190,17 +3334,7 @@ void ImFont::BuildLookupTable()
SetGlyphVisible ( ( ImWchar ) ' ' , false ) ;
SetGlyphVisible ( ( ImWchar ) ' \t ' , false ) ;
// Ellipsis character is required for rendering elided text. We prefer using U+2026 (horizontal ellipsis).
// However some old fonts may contain ellipsis at U+0085. Here we auto-detect most suitable ellipsis character.
// FIXME: Note that 0x2026 is rarely included in our font ranges. Because of this we are more likely to use three individual dots.
const ImWchar ellipsis_chars [ ] = { ( ImWchar ) 0x2026 , ( ImWchar ) 0x0085 } ;
const ImWchar dots_chars [ ] = { ( ImWchar ) ' . ' , ( ImWchar ) 0xFF0E } ;
if ( EllipsisChar = = ( ImWchar ) - 1 )
EllipsisChar = FindFirstExistingGlyph ( this , ellipsis_chars , IM_ARRAYSIZE ( ellipsis_chars ) ) ;
if ( DotChar = = ( ImWchar ) - 1 )
DotChar = FindFirstExistingGlyph ( this , dots_chars , IM_ARRAYSIZE ( dots_chars ) ) ;
// Setup fallback character
// Setup Fallback character
const ImWchar fallback_chars [ ] = { ( ImWchar ) IM_UNICODE_CODEPOINT_INVALID , ( ImWchar ) ' ? ' , ( ImWchar ) ' ' } ;
FallbackGlyph = FindGlyphNoFallback ( FallbackChar ) ;
if ( FallbackGlyph = = NULL )
@ -3213,11 +3347,32 @@ void ImFont::BuildLookupTable()
FallbackChar = ( ImWchar ) FallbackGlyph - > Codepoint ;
}
}
FallbackAdvanceX = FallbackGlyph - > AdvanceX ;
for ( int i = 0 ; i < max_codepoint + 1 ; i + + )
if ( IndexAdvanceX [ i ] < 0.0f )
IndexAdvanceX [ i ] = FallbackAdvanceX ;
// Setup Ellipsis character. It is required for rendering elided text. We prefer using U+2026 (horizontal ellipsis).
// However some old fonts may contain ellipsis at U+0085. Here we auto-detect most suitable ellipsis character.
// FIXME: Note that 0x2026 is rarely included in our font ranges. Because of this we are more likely to use three individual dots.
const ImWchar ellipsis_chars [ ] = { ( ImWchar ) 0x2026 , ( ImWchar ) 0x0085 } ;
const ImWchar dots_chars [ ] = { ( ImWchar ) ' . ' , ( ImWchar ) 0xFF0E } ;
if ( EllipsisChar = = ( ImWchar ) - 1 )
EllipsisChar = FindFirstExistingGlyph ( this , ellipsis_chars , IM_ARRAYSIZE ( ellipsis_chars ) ) ;
const ImWchar dot_char = FindFirstExistingGlyph ( this , dots_chars , IM_ARRAYSIZE ( dots_chars ) ) ;
if ( EllipsisChar ! = ( ImWchar ) - 1 )
{
EllipsisCharCount = 1 ;
EllipsisWidth = EllipsisCharStep = FindGlyph ( EllipsisChar ) - > X1 ;
}
else if ( dot_char ! = ( ImWchar ) - 1 )
{
const ImFontGlyph * glyph = FindGlyph ( dot_char ) ;
EllipsisChar = dot_char ;
EllipsisCharCount = 3 ;
EllipsisCharStep = ( glyph - > X1 - glyph - > X0 ) + 1.0f ;
EllipsisWidth = EllipsisCharStep * 3.0f - 1.0f ;
}
}
// API is designed this way to avoid exposing the 4K page size
@ -3260,7 +3415,7 @@ void ImFont::AddGlyph(const ImFontConfig* cfg, ImWchar codepoint, float x0, floa
advance_x = ImClamp ( advance_x , cfg - > GlyphMinAdvanceX , cfg - > GlyphMaxAdvanceX ) ;
if ( advance_x ! = advance_x_original )
{
float char_off_x = cfg - > PixelSnapH ? Im Floor ( ( advance_x - advance_x_original ) * 0.5f ) : ( advance_x - advance_x_original ) * 0.5f ;
float char_off_x = cfg - > PixelSnapH ? Im Trunc ( ( advance_x - advance_x_original ) * 0.5f ) : ( advance_x - advance_x_original ) * 0.5f ;
x0 + = char_off_x ;
x1 + = char_off_x ;
}
@ -3330,11 +3485,21 @@ const ImFontGlyph* ImFont::FindGlyphNoFallback(ImWchar c) const
return & Glyphs . Data [ i ] ;
}
const char * ImFont : : CalcWordWrapPositionA ( float scale , const char * text , const char * text_end , float wrap_width ) const
// Wrapping skips upcoming blanks
static inline const char * CalcWordWrapNextLineStartA ( const char * text , const char * text_end )
{
// Simple word-wrapping for English, not full-featured. Please submit failing cases!
// FIXME: Much possible improvements (don't cut things like "word !", "word!!!" but cut within "word,,,,", more sensible support for punctuations, support for Unicode punctuations, etc.)
while ( text < text_end & & ImCharIsBlankA ( * text ) )
text + + ;
if ( * text = = ' \n ' )
text + + ;
return text ;
}
// Simple word-wrapping for English, not full-featured. Please submit failing cases!
// This will return the next location to wrap from. If no wrapping if necessary, this will fast-forward to e.g. text_end.
// FIXME: Much possible improvements (don't cut things like "word !", "word!!!" but cut within "word,,,,", more sensible support for punctuations, support for Unicode punctuations, etc.)
const char * ImFont : : CalcWordWrapPositionA ( float scale , const char * text , const char * text_end , float wrap_width ) const
{
// For references, possible wrap point marked with ^
// "aaa bbb, ccc,ddd. eee fff. ggg!"
// ^ ^ ^ ^ ^__ ^ ^
@ -3346,7 +3511,6 @@ const char* ImFont::CalcWordWrapPositionA(float scale, const char* text, const c
// Cut words that cannot possibly fit within one line.
// e.g.: "The tropical fish" with ~5 characters worth of width --> "The tr" "opical" "fish"
float line_width = 0.0f ;
float word_width = 0.0f ;
float blank_width = 0.0f ;
@ -3357,6 +3521,7 @@ const char* ImFont::CalcWordWrapPositionA(float scale, const char* text, const c
bool inside_word = true ;
const char * s = text ;
IM_ASSERT ( text_end ! = NULL ) ;
while ( s < text_end )
{
unsigned int c = ( unsigned int ) * s ;
@ -3365,8 +3530,6 @@ const char* ImFont::CalcWordWrapPositionA(float scale, const char* text, const c
next_s = s + 1 ;
else
next_s = s + ImTextCharFromUtf8 ( & c , s , text_end ) ;
if ( c = = 0 )
break ;
if ( c < 32 )
{
@ -3426,6 +3589,10 @@ const char* ImFont::CalcWordWrapPositionA(float scale, const char* text, const c
s = next_s ;
}
// Wrap_width is too small to fit anything. Force displaying 1 character to minimize the height discontinuity.
// +1 may not be a character start point in UTF-8 but it's ok because caller loops use (text >= word_wrap_eol).
if ( s = = text & & text < text_end )
return s + 1 ;
return s ;
}
@ -3450,11 +3617,7 @@ ImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, cons
{
// Calculate how far we can render. Requires two passes on the string data but keeps the code simple and not intrusive for what's essentially an uncommon feature.
if ( ! word_wrap_eol )
{
word_wrap_eol = CalcWordWrapPositionA ( scale , s , text_end , wrap_width - line_width ) ;
if ( word_wrap_eol = = s ) // Wrap_width is too small to fit anything. Force displaying 1 character to minimize the height discontinuity.
word_wrap_eol + + ; // +1 may not be a character start point in UTF-8 but it's ok because we use s >= word_wrap_eol below
}
if ( s > = word_wrap_eol )
{
@ -3463,13 +3626,7 @@ ImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, cons
text_size . y + = line_height ;
line_width = 0.0f ;
word_wrap_eol = NULL ;
// Wrapping skips upcoming blanks
while ( s < text_end )
{
const char c = * s ;
if ( ImCharIsBlankA ( c ) ) { s + + ; } else if ( c = = ' \n ' ) { s + + ; break ; } else { break ; }
}
s = CalcWordWrapNextLineStartA ( s , text_end ) ; // Wrapping skips upcoming blanks
continue ;
}
}
@ -3478,15 +3635,9 @@ ImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, cons
const char * prev_s = s ;
unsigned int c = ( unsigned int ) * s ;
if ( c < 0x80 )
{
s + = 1 ;
}
else
{
s + = ImTextCharFromUtf8 ( & c , s , text_end ) ;
if ( c = = 0 ) // Malformed UTF-8?
break ;
}
if ( c < 32 )
{
@ -3532,8 +3683,8 @@ void ImFont::RenderChar(ImDrawList* draw_list, float size, const ImVec2& pos, Im
if ( glyph - > Colored )
col | = ~ IM_COL32_A_MASK ;
float scale = ( size > = 0.0f ) ? ( size / FontSize ) : 1.0f ;
float x = IM_ FLOOR ( pos . x ) ;
float y = IM_ FLOOR ( pos . y ) ;
float x = IM_ TRUNC ( pos . x ) ;
float y = IM_ TRUNC ( pos . y ) ;
draw_list - > PrimReserve ( 6 , 4 ) ;
draw_list - > PrimRectUV ( ImVec2 ( x + glyph - > X0 * scale , y + glyph - > Y0 * scale ) , ImVec2 ( x + glyph - > X1 * scale , y + glyph - > Y1 * scale ) , ImVec2 ( glyph - > U0 , glyph - > V0 ) , ImVec2 ( glyph - > U1 , glyph - > V1 ) , col ) ;
}
@ -3545,8 +3696,8 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, Im
text_end = text_begin + strlen ( text_begin ) ; // ImGui:: functions generally already provides a valid text_end, so this is merely to handle direct calls.
// Align to be pixel perfect
float x = IM_ FLOOR ( pos . x ) ;
float y = IM_ FLOOR ( pos . y ) ;
float x = IM_ TRUNC ( pos . x ) ;
float y = IM_ TRUNC ( pos . y ) ;
if ( y > clip_rect . w )
return ;
@ -3554,15 +3705,25 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, Im
const float scale = size / FontSize ;
const float line_height = FontSize * scale ;
const bool word_wrap_enabled = ( wrap_width > 0.0f ) ;
const char * word_wrap_eol = NULL ;
// Fast-forward to first visible line
const char * s = text_begin ;
if ( y + line_height < clip_rect . y & & ! word_wrap_enabled )
if ( y + line_height < clip_rect . y )
while ( y + line_height < clip_rect . y & & s < text_end )
{
s = ( const char * ) memchr ( s , ' \n ' , text_end - s ) ;
s = s ? s + 1 : text_end ;
const char * line_end = ( const char * ) memchr ( s , ' \n ' , text_end - s ) ;
if ( word_wrap_enabled )
{
// FIXME-OPT: This is not optimal as do first do a search for \n before calling CalcWordWrapPositionA().
// If the specs for CalcWordWrapPositionA() were reworked to optionally return on \n we could combine both.
// However it is still better than nothing performing the fast-forward!
s = CalcWordWrapPositionA ( scale , s , line_end ? line_end : text_end , wrap_width ) ;
s = CalcWordWrapNextLineStartA ( s , text_end ) ;
}
else
{
s = line_end ? line_end + 1 : text_end ;
}
y + = line_height ;
}
@ -3588,12 +3749,12 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, Im
const int idx_count_max = ( int ) ( text_end - s ) * 6 ;
const int idx_expected_size = draw_list - > IdxBuffer . Size + idx_count_max ;
draw_list - > PrimReserve ( idx_count_max , vtx_count_max ) ;
ImDrawVert * vtx_write = draw_list - > _VtxWritePtr ;
ImDrawIdx * idx_write = draw_list - > _IdxWritePtr ;
unsigned int vtx_current_idx = draw_list - > _VtxCurrentIdx ;
ImDrawVert * vtx_write = draw_list - > _VtxWritePtr ;
ImDrawIdx * idx_write = draw_list - > _IdxWritePtr ;
unsigned int vtx_index = draw_list - > _VtxCurrentIdx ;
const ImU32 col_untinted = col | ~ IM_COL32_A_MASK ;
const char * word_wrap_eol = NULL ;
while ( s < text_end )
{
@ -3601,24 +3762,14 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, Im
{
// Calculate how far we can render. Requires two passes on the string data but keeps the code simple and not intrusive for what's essentially an uncommon feature.
if ( ! word_wrap_eol )
{
word_wrap_eol = CalcWordWrapPositionA ( scale , s , text_end , wrap_width - ( x - start_x ) ) ;
if ( word_wrap_eol = = s ) // Wrap_width is too small to fit anything. Force displaying 1 character to minimize the height discontinuity.
word_wrap_eol + + ; // +1 may not be a character start point in UTF-8 but it's ok because we use s >= word_wrap_eol below
}
if ( s > = word_wrap_eol )
{
x = start_x ;
y + = line_height ;
word_wrap_eol = NULL ;
// Wrapping skips upcoming blanks
while ( s < text_end )
{
const char c = * s ;
if ( ImCharIsBlankA ( c ) ) { s + + ; } else if ( c = = ' \n ' ) { s + + ; break ; } else { break ; }
}
s = CalcWordWrapNextLineStartA ( s , text_end ) ; // Wrapping skips upcoming blanks
continue ;
}
}
@ -3626,15 +3777,9 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, Im
// Decode and advance source
unsigned int c = ( unsigned int ) * s ;
if ( c < 0x80 )
{
s + = 1 ;
}
else
{
s + = ImTextCharFromUtf8 ( & c , s , text_end ) ;
if ( c = = 0 ) // Malformed UTF-8?
break ;
}
if ( c < 32 )
{
@ -3705,14 +3850,14 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, Im
// We are NOT calling PrimRectUV() here because non-inlined causes too much overhead in a debug builds. Inlined here:
{
idx_write [ 0 ] = ( ImDrawIdx ) ( vtx_current_idx ) ; idx_write [ 1 ] = ( ImDrawIdx ) ( vtx_current_idx + 1 ) ; idx_write [ 2 ] = ( ImDrawIdx ) ( vtx_current_idx + 2 ) ;
idx_write [ 3 ] = ( ImDrawIdx ) ( vtx_current_idx ) ; idx_write [ 4 ] = ( ImDrawIdx ) ( vtx_current_idx + 2 ) ; idx_write [ 5 ] = ( ImDrawIdx ) ( vtx_current_idx + 3 ) ;
vtx_write [ 0 ] . pos . x = x1 ; vtx_write [ 0 ] . pos . y = y1 ; vtx_write [ 0 ] . col = glyph_col ; vtx_write [ 0 ] . uv . x = u1 ; vtx_write [ 0 ] . uv . y = v1 ;
vtx_write [ 1 ] . pos . x = x2 ; vtx_write [ 1 ] . pos . y = y1 ; vtx_write [ 1 ] . col = glyph_col ; vtx_write [ 1 ] . uv . x = u2 ; vtx_write [ 1 ] . uv . y = v1 ;
vtx_write [ 2 ] . pos . x = x2 ; vtx_write [ 2 ] . pos . y = y2 ; vtx_write [ 2 ] . col = glyph_col ; vtx_write [ 2 ] . uv . x = u2 ; vtx_write [ 2 ] . uv . y = v2 ;
vtx_write [ 3 ] . pos . x = x1 ; vtx_write [ 3 ] . pos . y = y2 ; vtx_write [ 3 ] . col = glyph_col ; vtx_write [ 3 ] . uv . x = u1 ; vtx_write [ 3 ] . uv . y = v2 ;
idx_write [ 0 ] = ( ImDrawIdx ) ( vtx_index ) ; idx_write [ 1 ] = ( ImDrawIdx ) ( vtx_index + 1 ) ; idx_write [ 2 ] = ( ImDrawIdx ) ( vtx_index + 2 ) ;
idx_write [ 3 ] = ( ImDrawIdx ) ( vtx_index ) ; idx_write [ 4 ] = ( ImDrawIdx ) ( vtx_index + 2 ) ; idx_write [ 5 ] = ( ImDrawIdx ) ( vtx_index + 3 ) ;
vtx_write + = 4 ;
vtx_current_idx + = 4 ;
vtx_ inde x + = 4 ;
idx_write + = 6 ;
}
}
@ -3726,7 +3871,7 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, Im
draw_list - > CmdBuffer [ draw_list - > CmdBuffer . Size - 1 ] . ElemCount - = ( idx_expected_size - draw_list - > IdxBuffer . Size ) ;
draw_list - > _VtxWritePtr = vtx_write ;
draw_list - > _IdxWritePtr = idx_write ;
draw_list - > _VtxCurrentIdx = vtx_ current_id x;
draw_list - > _VtxCurrentIdx = vtx_ inde x;
}
//-----------------------------------------------------------------------------
@ -3778,6 +3923,7 @@ void ImGui::RenderArrow(ImDrawList* draw_list, ImVec2 pos, ImU32 col, ImGuiDir d
void ImGui : : RenderBullet ( ImDrawList * draw_list , ImVec2 pos , ImU32 col )
{
// FIXME-OPT: This should be baked in font.
draw_list - > AddCircleFilled ( pos , draw_list - > _Data - > FontSize * 0.20f , col , 8 ) ;
}
@ -4059,8 +4205,8 @@ static unsigned int stb_decompress(unsigned char *output, const unsigned char *i
//-----------------------------------------------------------------------------
// ProggyClean.ttf
// Copyright (c) 2004, 2005 Tristan Grimmer
// MIT license (see License.txt in http://www. upperbounds.net/download/ProggyClean.ttf.zip )
// Download and more information at http:// upperbounds.net
// MIT license (see License.txt in http://www. proggyfonts.net/index.php?menu=download )
// Download and more information at http:// www.proggyfonts.net or http://upperboundsinteractive.com/fonts.php
//-----------------------------------------------------------------------------
// File: 'ProggyClean.ttf' (41208 bytes)
// Exported using misc/fonts/binary_to_compressed_c.cpp (with compression + base85 string encoding).