Revision 932a86a1
Added by david.sorber over 1 year ago
| src/imgui/imgui_draw.cpp | ||
|---|---|---|
|
// dear imgui, v1.90.2 WIP
|
||
|
// dear imgui, v1.91.5
|
||
|
// (drawing and font code)
|
||
|
|
||
|
/*
|
||
| ... | ... | |
|
// [SECTION] STB libraries implementation
|
||
|
// [SECTION] Style functions
|
||
|
// [SECTION] ImDrawList
|
||
|
// [SECTION] ImTriangulator, ImDrawList concave polygon fill
|
||
|
// [SECTION] ImDrawListSplitter
|
||
|
// [SECTION] ImDrawData
|
||
|
// [SECTION] Helpers ShadeVertsXXX functions
|
||
| ... | ... | |
|
#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
|
||
|
#pragma clang diagnostic ignored "-Wunsafe-buffer-usage" // warning: 'xxx' is an unsafe pointer used for buffer access
|
||
|
#pragma clang diagnostic ignored "-Wnontrivial-memaccess" // warning: first argument in call to 'memset' is a pointer to non-trivially copyable type
|
||
|
#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
|
||
|
#pragma GCC diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function
|
||
|
#pragma GCC diagnostic ignored "-Wconversion" // warning: conversion to 'xxxx' from 'xxxx' may alter its value
|
||
|
#pragma GCC diagnostic ignored "-Wstack-protector" // warning: stack protector not protecting local variables: variable length buffer
|
||
|
#pragma GCC diagnostic ignored "-Wclass-memaccess" // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead
|
||
|
#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
|
||
|
#pragma GCC diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function
|
||
|
#pragma GCC diagnostic ignored "-Wconversion" // warning: conversion to 'xxxx' from 'xxxx' may alter its value
|
||
|
#pragma GCC diagnostic ignored "-Wstack-protector" // warning: stack protector not protecting local variables: variable length buffer
|
||
|
#pragma GCC diagnostic ignored "-Wclass-memaccess" // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead
|
||
|
#endif
|
||
|
|
||
|
//-------------------------------------------------------------------------
|
||
| ... | ... | |
|
|
||
|
#if defined(__clang__)
|
||
|
#pragma clang diagnostic push
|
||
|
#pragma clang diagnostic ignored "-Wunused-function"
|
||
|
#pragma clang diagnostic ignored "-Wunused-function" // warning: 'xxxx' defined but not used
|
||
|
#pragma clang diagnostic ignored "-Wmissing-prototypes"
|
||
|
#pragma clang diagnostic ignored "-Wimplicit-fallthrough"
|
||
|
#pragma clang diagnostic ignored "-Wcast-qual" // warning: cast from 'const xxxx *' to 'xxx *' drops const qualifier
|
||
| ... | ... | |
|
colors[ImGuiCol_ResizeGrip] = ImVec4(0.26f, 0.59f, 0.98f, 0.20f);
|
||
|
colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.26f, 0.59f, 0.98f, 0.67f);
|
||
|
colors[ImGuiCol_ResizeGripActive] = ImVec4(0.26f, 0.59f, 0.98f, 0.95f);
|
||
|
colors[ImGuiCol_Tab] = ImLerp(colors[ImGuiCol_Header], colors[ImGuiCol_TitleBgActive], 0.80f);
|
||
|
colors[ImGuiCol_TabHovered] = colors[ImGuiCol_HeaderHovered];
|
||
|
colors[ImGuiCol_TabActive] = ImLerp(colors[ImGuiCol_HeaderActive], colors[ImGuiCol_TitleBgActive], 0.60f);
|
||
|
colors[ImGuiCol_TabUnfocused] = ImLerp(colors[ImGuiCol_Tab], colors[ImGuiCol_TitleBg], 0.80f);
|
||
|
colors[ImGuiCol_TabUnfocusedActive] = ImLerp(colors[ImGuiCol_TabActive], colors[ImGuiCol_TitleBg], 0.40f);
|
||
|
colors[ImGuiCol_Tab] = ImLerp(colors[ImGuiCol_Header], colors[ImGuiCol_TitleBgActive], 0.80f);
|
||
|
colors[ImGuiCol_TabSelected] = ImLerp(colors[ImGuiCol_HeaderActive], colors[ImGuiCol_TitleBgActive], 0.60f);
|
||
|
colors[ImGuiCol_TabSelectedOverline] = colors[ImGuiCol_HeaderActive];
|
||
|
colors[ImGuiCol_TabDimmed] = ImLerp(colors[ImGuiCol_Tab], colors[ImGuiCol_TitleBg], 0.80f);
|
||
|
colors[ImGuiCol_TabDimmedSelected] = ImLerp(colors[ImGuiCol_TabSelected], colors[ImGuiCol_TitleBg], 0.40f);
|
||
|
colors[ImGuiCol_TabDimmedSelectedOverline] = ImVec4(0.50f, 0.50f, 0.50f, 0.00f);
|
||
|
colors[ImGuiCol_PlotLines] = ImVec4(0.61f, 0.61f, 0.61f, 1.00f);
|
||
|
colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.43f, 0.35f, 1.00f);
|
||
|
colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
|
||
| ... | ... | |
|
colors[ImGuiCol_TableBorderLight] = ImVec4(0.23f, 0.23f, 0.25f, 1.00f); // Prefer using Alpha=1.0 here
|
||
|
colors[ImGuiCol_TableRowBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
|
||
|
colors[ImGuiCol_TableRowBgAlt] = ImVec4(1.00f, 1.00f, 1.00f, 0.06f);
|
||
|
colors[ImGuiCol_TextLink] = colors[ImGuiCol_HeaderActive];
|
||
|
colors[ImGuiCol_TextSelectedBg] = ImVec4(0.26f, 0.59f, 0.98f, 0.35f);
|
||
|
colors[ImGuiCol_DragDropTarget] = ImVec4(1.00f, 1.00f, 0.00f, 0.90f);
|
||
|
colors[ImGuiCol_NavHighlight] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);
|
||
|
colors[ImGuiCol_NavCursor] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);
|
||
|
colors[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00f, 1.00f, 1.00f, 0.70f);
|
||
|
colors[ImGuiCol_NavWindowingDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.20f);
|
||
|
colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.35f);
|
||
| ... | ... | |
|
colors[ImGuiCol_ResizeGrip] = ImVec4(1.00f, 1.00f, 1.00f, 0.10f);
|
||
|
colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.78f, 0.82f, 1.00f, 0.60f);
|
||
|
colors[ImGuiCol_ResizeGripActive] = ImVec4(0.78f, 0.82f, 1.00f, 0.90f);
|
||
|
colors[ImGuiCol_Tab] = ImLerp(colors[ImGuiCol_Header], colors[ImGuiCol_TitleBgActive], 0.80f);
|
||
|
colors[ImGuiCol_TabHovered] = colors[ImGuiCol_HeaderHovered];
|
||
|
colors[ImGuiCol_TabActive] = ImLerp(colors[ImGuiCol_HeaderActive], colors[ImGuiCol_TitleBgActive], 0.60f);
|
||
|
colors[ImGuiCol_TabUnfocused] = ImLerp(colors[ImGuiCol_Tab], colors[ImGuiCol_TitleBg], 0.80f);
|
||
|
colors[ImGuiCol_TabUnfocusedActive] = ImLerp(colors[ImGuiCol_TabActive], colors[ImGuiCol_TitleBg], 0.40f);
|
||
|
colors[ImGuiCol_Tab] = ImLerp(colors[ImGuiCol_Header], colors[ImGuiCol_TitleBgActive], 0.80f);
|
||
|
colors[ImGuiCol_TabSelected] = ImLerp(colors[ImGuiCol_HeaderActive], colors[ImGuiCol_TitleBgActive], 0.60f);
|
||
|
colors[ImGuiCol_TabSelectedOverline] = colors[ImGuiCol_HeaderActive];
|
||
|
colors[ImGuiCol_TabDimmed] = ImLerp(colors[ImGuiCol_Tab], colors[ImGuiCol_TitleBg], 0.80f);
|
||
|
colors[ImGuiCol_TabDimmedSelected] = ImLerp(colors[ImGuiCol_TabSelected], colors[ImGuiCol_TitleBg], 0.40f);
|
||
|
colors[ImGuiCol_TabDimmedSelectedOverline] = ImVec4(0.53f, 0.53f, 0.87f, 0.00f);
|
||
|
colors[ImGuiCol_PlotLines] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);
|
||
|
colors[ImGuiCol_PlotLinesHovered] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
|
||
|
colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
|
||
| ... | ... | |
|
colors[ImGuiCol_TableBorderLight] = ImVec4(0.26f, 0.26f, 0.28f, 1.00f); // Prefer using Alpha=1.0 here
|
||
|
colors[ImGuiCol_TableRowBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
|
||
|
colors[ImGuiCol_TableRowBgAlt] = ImVec4(1.00f, 1.00f, 1.00f, 0.07f);
|
||
|
colors[ImGuiCol_TextLink] = colors[ImGuiCol_HeaderActive];
|
||
|
colors[ImGuiCol_TextSelectedBg] = ImVec4(0.00f, 0.00f, 1.00f, 0.35f);
|
||
|
colors[ImGuiCol_DragDropTarget] = ImVec4(1.00f, 1.00f, 0.00f, 0.90f);
|
||
|
colors[ImGuiCol_NavHighlight] = colors[ImGuiCol_HeaderHovered];
|
||
|
colors[ImGuiCol_NavCursor] = colors[ImGuiCol_HeaderHovered];
|
||
|
colors[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00f, 1.00f, 1.00f, 0.70f);
|
||
|
colors[ImGuiCol_NavWindowingDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.20f);
|
||
|
colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.20f, 0.20f, 0.20f, 0.35f);
|
||
| ... | ... | |
|
colors[ImGuiCol_ResizeGrip] = ImVec4(0.35f, 0.35f, 0.35f, 0.17f);
|
||
|
colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.26f, 0.59f, 0.98f, 0.67f);
|
||
|
colors[ImGuiCol_ResizeGripActive] = ImVec4(0.26f, 0.59f, 0.98f, 0.95f);
|
||
|
colors[ImGuiCol_Tab] = ImLerp(colors[ImGuiCol_Header], colors[ImGuiCol_TitleBgActive], 0.90f);
|
||
|
colors[ImGuiCol_TabHovered] = colors[ImGuiCol_HeaderHovered];
|
||
|
colors[ImGuiCol_TabActive] = ImLerp(colors[ImGuiCol_HeaderActive], colors[ImGuiCol_TitleBgActive], 0.60f);
|
||
|
colors[ImGuiCol_TabUnfocused] = ImLerp(colors[ImGuiCol_Tab], colors[ImGuiCol_TitleBg], 0.80f);
|
||
|
colors[ImGuiCol_TabUnfocusedActive] = ImLerp(colors[ImGuiCol_TabActive], colors[ImGuiCol_TitleBg], 0.40f);
|
||
|
colors[ImGuiCol_Tab] = ImLerp(colors[ImGuiCol_Header], colors[ImGuiCol_TitleBgActive], 0.90f);
|
||
|
colors[ImGuiCol_TabSelected] = ImLerp(colors[ImGuiCol_HeaderActive], colors[ImGuiCol_TitleBgActive], 0.60f);
|
||
|
colors[ImGuiCol_TabSelectedOverline] = colors[ImGuiCol_HeaderActive];
|
||
|
colors[ImGuiCol_TabDimmed] = ImLerp(colors[ImGuiCol_Tab], colors[ImGuiCol_TitleBg], 0.80f);
|
||
|
colors[ImGuiCol_TabDimmedSelected] = ImLerp(colors[ImGuiCol_TabSelected], colors[ImGuiCol_TitleBg], 0.40f);
|
||
|
colors[ImGuiCol_TabDimmedSelectedOverline] = ImVec4(0.26f, 0.59f, 1.00f, 0.00f);
|
||
|
colors[ImGuiCol_PlotLines] = ImVec4(0.39f, 0.39f, 0.39f, 1.00f);
|
||
|
colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.43f, 0.35f, 1.00f);
|
||
|
colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
|
||
| ... | ... | |
|
colors[ImGuiCol_TableBorderLight] = ImVec4(0.68f, 0.68f, 0.74f, 1.00f); // Prefer using Alpha=1.0 here
|
||
|
colors[ImGuiCol_TableRowBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
|
||
|
colors[ImGuiCol_TableRowBgAlt] = ImVec4(0.30f, 0.30f, 0.30f, 0.09f);
|
||
|
colors[ImGuiCol_TextLink] = colors[ImGuiCol_HeaderActive];
|
||
|
colors[ImGuiCol_TextSelectedBg] = ImVec4(0.26f, 0.59f, 0.98f, 0.35f);
|
||
|
colors[ImGuiCol_DragDropTarget] = ImVec4(0.26f, 0.59f, 0.98f, 0.95f);
|
||
|
colors[ImGuiCol_NavHighlight] = colors[ImGuiCol_HeaderHovered];
|
||
|
colors[ImGuiCol_NavCursor] = colors[ImGuiCol_HeaderHovered];
|
||
|
colors[ImGuiCol_NavWindowingHighlight] = ImVec4(0.70f, 0.70f, 0.70f, 0.70f);
|
||
|
colors[ImGuiCol_NavWindowingDimBg] = ImVec4(0.20f, 0.20f, 0.20f, 0.20f);
|
||
|
colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.20f, 0.20f, 0.20f, 0.35f);
|
||
| ... | ... | |
|
}
|
||
|
|
||
|
// Initialize before use in a new frame. We always have a command ready in the buffer.
|
||
|
// In the majority of cases, you would want to call PushClipRect() and PushTextureID() after this.
|
||
|
void ImDrawList::_ResetForNewFrame()
|
||
|
{
|
||
|
// Verify that the ImDrawCmd fields we want to memcmp() are contiguous in memory.
|
||
| ... | ... | |
|
_IdxWritePtr = NULL;
|
||
|
_ClipRectStack.resize(0);
|
||
|
_TextureIdStack.resize(0);
|
||
|
_CallbacksDataBuf.resize(0);
|
||
|
_Path.resize(0);
|
||
|
_Splitter.Clear();
|
||
|
CmdBuffer.push_back(ImDrawCmd());
|
||
| ... | ... | |
|
_IdxWritePtr = NULL;
|
||
|
_ClipRectStack.clear();
|
||
|
_TextureIdStack.clear();
|
||
|
_CallbacksDataBuf.clear();
|
||
|
_Path.clear();
|
||
|
_Splitter.ClearFreeMemory();
|
||
|
}
|
||
| ... | ... | |
|
}
|
||
|
}
|
||
|
|
||
|
void ImDrawList::AddCallback(ImDrawCallback callback, void* callback_data)
|
||
|
void ImDrawList::AddCallback(ImDrawCallback callback, void* userdata, size_t userdata_size)
|
||
|
{
|
||
|
IM_ASSERT_PARANOID(CmdBuffer.Size > 0);
|
||
|
ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1];
|
||
| ... | ... | |
|
AddDrawCmd();
|
||
|
curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1];
|
||
|
}
|
||
|
|
||
|
curr_cmd->UserCallback = callback;
|
||
|
curr_cmd->UserCallbackData = callback_data;
|
||
|
if (userdata_size == 0)
|
||
|
{
|
||
|
// Store user data directly in command (no indirection)
|
||
|
curr_cmd->UserCallbackData = userdata;
|
||
|
curr_cmd->UserCallbackDataSize = 0;
|
||
|
curr_cmd->UserCallbackDataOffset = -1;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Copy and store user data in a buffer
|
||
|
IM_ASSERT(userdata != NULL);
|
||
|
IM_ASSERT(userdata_size < (1u << 31));
|
||
|
curr_cmd->UserCallbackData = NULL; // Will be resolved during Render()
|
||
|
curr_cmd->UserCallbackDataSize = (int)userdata_size;
|
||
|
curr_cmd->UserCallbackDataOffset = _CallbacksDataBuf.Size;
|
||
|
_CallbacksDataBuf.resize(_CallbacksDataBuf.Size + (int)userdata_size);
|
||
|
memcpy(_CallbacksDataBuf.Data + (size_t)curr_cmd->UserCallbackDataOffset, userdata, userdata_size);
|
||
|
}
|
||
|
|
||
|
AddDrawCmd(); // Force a new command after us (see comment below)
|
||
|
}
|
||
| ... | ... | |
|
CmdBuffer.pop_back();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
curr_cmd->ClipRect = _CmdHeader.ClipRect;
|
||
|
}
|
||
|
|
||
| ... | ... | |
|
CmdBuffer.pop_back();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
curr_cmd->TextureId = _CmdHeader.TextureId;
|
||
|
}
|
||
|
|
||
| ... | ... | |
|
_OnChangedTextureID();
|
||
|
}
|
||
|
|
||
|
// This is used by ImGui::PushFont()/PopFont(). It works because we never use _TextureIdStack[] elsewhere than in PushTextureID()/PopTextureID().
|
||
|
void ImDrawList::_SetTextureID(ImTextureID texture_id)
|
||
|
{
|
||
|
if (_CmdHeader.TextureId == texture_id)
|
||
|
return;
|
||
|
_CmdHeader.TextureId = texture_id;
|
||
|
_OnChangedTextureID();
|
||
|
}
|
||
|
|
||
|
// Reserve space for a number of vertices and indices.
|
||
|
// You must finish filling your reserved data before calling PrimReserve() again, as it may reallocate or
|
||
|
// submit the intermediate results. PrimUnreserve() can be used to release unused allocations.
|
||
| ... | ... | |
|
}
|
||
|
}
|
||
|
|
||
|
void ImDrawList::PathEllipticalArcTo(const ImVec2& center, float radius_x, float radius_y, float rot, float a_min, float a_max, int num_segments)
|
||
|
void ImDrawList::PathEllipticalArcTo(const ImVec2& center, const ImVec2& radius, 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.
|
||
|
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));
|
||
|
|
||
| ... | ... | |
|
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;
|
||
|
ImVec2 point(ImCos(a) * radius.x, ImSin(a) * radius.y);
|
||
|
const ImVec2 rel((point.x * cos_rot) - (point.y * sin_rot), (point.x * sin_rot) + (point.y * cos_rot));
|
||
|
point.x = rel.x + center.x;
|
||
|
point.y = rel.y + center.y;
|
||
|
_Path.push_back(point);
|
||
|
}
|
||
|
}
|
||
| ... | ... | |
|
}
|
||
|
|
||
|
// Ellipse
|
||
|
void ImDrawList::AddEllipse(const ImVec2& center, float radius_x, float radius_y, ImU32 col, float rot, int num_segments, float thickness)
|
||
|
void ImDrawList::AddEllipse(const ImVec2& center, const ImVec2& radius, 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.
|
||
|
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);
|
||
|
PathEllipticalArcTo(center, radius, 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)
|
||
|
void ImDrawList::AddEllipseFilled(const ImVec2& center, const ImVec2& radius, 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.
|
||
|
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);
|
||
|
PathEllipticalArcTo(center, radius, rot, 0.0f, a_max, num_segments - 1);
|
||
|
PathFillConvex(col);
|
||
|
}
|
||
|
|
||
| ... | ... | |
|
PathStroke(col, 0, thickness);
|
||
|
}
|
||
|
|
||
|
void ImDrawList::AddText(const ImFont* font, float font_size, const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end, float wrap_width, const ImVec4* cpu_fine_clip_rect)
|
||
|
void ImDrawList::AddText(ImFont* font, float font_size, const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end, float wrap_width, const ImVec4* cpu_fine_clip_rect)
|
||
|
{
|
||
|
if ((col & IM_COL32_A_MASK) == 0)
|
||
|
return;
|
||
|
|
||
|
// Accept null ranges
|
||
|
if (text_begin == text_end || text_begin[0] == 0)
|
||
|
return;
|
||
|
if (text_end == NULL)
|
||
|
text_end = text_begin + strlen(text_begin);
|
||
|
if (text_begin == text_end)
|
||
|
return;
|
||
|
|
||
|
// Pull default font/size from the shared ImDrawListSharedData instance
|
||
|
if (font == NULL)
|
||
| ... | ... | |
|
PopTextureID();
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// [SECTION] ImTriangulator, ImDrawList concave polygon fill
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Triangulate concave polygons. Based on "Triangulation by Ear Clipping" paper, O(N^2) complexity.
|
||
|
// Reference: https://www.geometrictools.com/Documentation/TriangulationByEarClipping.pdf
|
||
|
// Provided as a convenience for user but not used by main library.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// - ImTriangulator [Internal]
|
||
|
// - AddConcavePolyFilled()
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
enum ImTriangulatorNodeType
|
||
|
{
|
||
|
ImTriangulatorNodeType_Convex,
|
||
|
ImTriangulatorNodeType_Ear,
|
||
|
ImTriangulatorNodeType_Reflex
|
||
|
};
|
||
|
|
||
|
struct ImTriangulatorNode
|
||
|
{
|
||
|
ImTriangulatorNodeType Type;
|
||
|
int Index;
|
||
|
ImVec2 Pos;
|
||
|
ImTriangulatorNode* Next;
|
||
|
ImTriangulatorNode* Prev;
|
||
|
|
||
|
void Unlink() { Next->Prev = Prev; Prev->Next = Next; }
|
||
|
};
|
||
|
|
||
|
struct ImTriangulatorNodeSpan
|
||
|
{
|
||
|
ImTriangulatorNode** Data = NULL;
|
||
|
int Size = 0;
|
||
|
|
||
|
void push_back(ImTriangulatorNode* node) { Data[Size++] = node; }
|
||
|
void find_erase_unsorted(int idx) { for (int i = Size - 1; i >= 0; i--) if (Data[i]->Index == idx) { Data[i] = Data[Size - 1]; Size--; return; } }
|
||
|
};
|
||
|
|
||
|
struct ImTriangulator
|
||
|
{
|
||
|
static int EstimateTriangleCount(int points_count) { return (points_count < 3) ? 0 : points_count - 2; }
|
||
|
static int EstimateScratchBufferSize(int points_count) { return sizeof(ImTriangulatorNode) * points_count + sizeof(ImTriangulatorNode*) * points_count * 2; }
|
||
|
|
||
|
void Init(const ImVec2* points, int points_count, void* scratch_buffer);
|
||
|
void GetNextTriangle(unsigned int out_triangle[3]); // Return relative indexes for next triangle
|
||
|
|
||
|
// Internal functions
|
||
|
void BuildNodes(const ImVec2* points, int points_count);
|
||
|
void BuildReflexes();
|
||
|
void BuildEars();
|
||
|
void FlipNodeList();
|
||
|
bool IsEar(int i0, int i1, int i2, const ImVec2& v0, const ImVec2& v1, const ImVec2& v2) const;
|
||
|
void ReclassifyNode(ImTriangulatorNode* node);
|
||
|
|
||
|
// Internal members
|
||
|
int _TrianglesLeft = 0;
|
||
|
ImTriangulatorNode* _Nodes = NULL;
|
||
|
ImTriangulatorNodeSpan _Ears;
|
||
|
ImTriangulatorNodeSpan _Reflexes;
|
||
|
};
|
||
|
|
||
|
// Distribute storage for nodes, ears and reflexes.
|
||
|
// FIXME-OPT: if everything is convex, we could report it to caller and let it switch to an convex renderer
|
||
|
// (this would require first building reflexes to bail to convex if empty, without even building nodes)
|
||
|
void ImTriangulator::Init(const ImVec2* points, int points_count, void* scratch_buffer)
|
||
|
{
|
||
|
IM_ASSERT(scratch_buffer != NULL && points_count >= 3);
|
||
|
_TrianglesLeft = EstimateTriangleCount(points_count);
|
||
|
_Nodes = (ImTriangulatorNode*)scratch_buffer; // points_count x Node
|
||
|
_Ears.Data = (ImTriangulatorNode**)(_Nodes + points_count); // points_count x Node*
|
||
|
_Reflexes.Data = (ImTriangulatorNode**)(_Nodes + points_count) + points_count; // points_count x Node*
|
||
|
BuildNodes(points, points_count);
|
||
|
BuildReflexes();
|
||
|
BuildEars();
|
||
|
}
|
||
|
|
||
|
void ImTriangulator::BuildNodes(const ImVec2* points, int points_count)
|
||
|
{
|
||
|
for (int i = 0; i < points_count; i++)
|
||
|
{
|
||
|
_Nodes[i].Type = ImTriangulatorNodeType_Convex;
|
||
|
_Nodes[i].Index = i;
|
||
|
_Nodes[i].Pos = points[i];
|
||
|
_Nodes[i].Next = _Nodes + i + 1;
|
||
|
_Nodes[i].Prev = _Nodes + i - 1;
|
||
|
}
|
||
|
_Nodes[0].Prev = _Nodes + points_count - 1;
|
||
|
_Nodes[points_count - 1].Next = _Nodes;
|
||
|
}
|
||
|
|
||
|
void ImTriangulator::BuildReflexes()
|
||
|
{
|
||
|
ImTriangulatorNode* n1 = _Nodes;
|
||
|
for (int i = _TrianglesLeft; i >= 0; i--, n1 = n1->Next)
|
||
|
{
|
||
|
if (ImTriangleIsClockwise(n1->Prev->Pos, n1->Pos, n1->Next->Pos))
|
||
|
continue;
|
||
|
n1->Type = ImTriangulatorNodeType_Reflex;
|
||
|
_Reflexes.push_back(n1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void ImTriangulator::BuildEars()
|
||
|
{
|
||
|
ImTriangulatorNode* n1 = _Nodes;
|
||
|
for (int i = _TrianglesLeft; i >= 0; i--, n1 = n1->Next)
|
||
|
{
|
||
|
if (n1->Type != ImTriangulatorNodeType_Convex)
|
||
|
continue;
|
||
|
if (!IsEar(n1->Prev->Index, n1->Index, n1->Next->Index, n1->Prev->Pos, n1->Pos, n1->Next->Pos))
|
||
|
continue;
|
||
|
n1->Type = ImTriangulatorNodeType_Ear;
|
||
|
_Ears.push_back(n1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void ImTriangulator::GetNextTriangle(unsigned int out_triangle[3])
|
||
|
{
|
||
|
if (_Ears.Size == 0)
|
||
|
{
|
||
|
FlipNodeList();
|
||
|
|
||
|
ImTriangulatorNode* node = _Nodes;
|
||
|
for (int i = _TrianglesLeft; i >= 0; i--, node = node->Next)
|
||
|
node->Type = ImTriangulatorNodeType_Convex;
|
||
|
_Reflexes.Size = 0;
|
||
|
BuildReflexes();
|
||
|
BuildEars();
|
||
|
|
||
|
// If we still don't have ears, it means geometry is degenerated.
|
||
|
if (_Ears.Size == 0)
|
||
|
{
|
||
|
// Return first triangle available, mimicking the behavior of convex fill.
|
||
|
IM_ASSERT(_TrianglesLeft > 0); // Geometry is degenerated
|
||
|
_Ears.Data[0] = _Nodes;
|
||
|
_Ears.Size = 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ImTriangulatorNode* ear = _Ears.Data[--_Ears.Size];
|
||
|
out_triangle[0] = ear->Prev->Index;
|
||
|
out_triangle[1] = ear->Index;
|
||
|
out_triangle[2] = ear->Next->Index;
|
||
|
|
||
|
ear->Unlink();
|
||
|
if (ear == _Nodes)
|
||
|
_Nodes = ear->Next;
|
||
|
|
||
|
ReclassifyNode(ear->Prev);
|
||
|
ReclassifyNode(ear->Next);
|
||
|
_TrianglesLeft--;
|
||
|
}
|
||
|
|
||
|
void ImTriangulator::FlipNodeList()
|
||
|
{
|
||
|
ImTriangulatorNode* prev = _Nodes;
|
||
|
ImTriangulatorNode* temp = _Nodes;
|
||
|
ImTriangulatorNode* current = _Nodes->Next;
|
||
|
prev->Next = prev;
|
||
|
prev->Prev = prev;
|
||
|
while (current != _Nodes)
|
||
|
{
|
||
|
temp = current->Next;
|
||
|
|
||
|
current->Next = prev;
|
||
|
prev->Prev = current;
|
||
|
_Nodes->Next = current;
|
||
|
current->Prev = _Nodes;
|
||
|
|
||
|
prev = current;
|
||
|
current = temp;
|
||
|
}
|
||
|
_Nodes = prev;
|
||
|
}
|
||
|
|
||
|
// A triangle is an ear is no other vertex is inside it. We can test reflexes vertices only (see reference algorithm)
|
||
|
bool ImTriangulator::IsEar(int i0, int i1, int i2, const ImVec2& v0, const ImVec2& v1, const ImVec2& v2) const
|
||
|
{
|
||
|
ImTriangulatorNode** p_end = _Reflexes.Data + _Reflexes.Size;
|
||
|
for (ImTriangulatorNode** p = _Reflexes.Data; p < p_end; p++)
|
||
|
{
|
||
|
ImTriangulatorNode* reflex = *p;
|
||
|
if (reflex->Index != i0 && reflex->Index != i1 && reflex->Index != i2)
|
||
|
if (ImTriangleContainsPoint(v0, v1, v2, reflex->Pos))
|
||
|
return false;
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
void ImTriangulator::ReclassifyNode(ImTriangulatorNode* n1)
|
||
|
{
|
||
|
// Classify node
|
||
|
ImTriangulatorNodeType type;
|
||
|
const ImTriangulatorNode* n0 = n1->Prev;
|
||
|
const ImTriangulatorNode* n2 = n1->Next;
|
||
|
if (!ImTriangleIsClockwise(n0->Pos, n1->Pos, n2->Pos))
|
||
|
type = ImTriangulatorNodeType_Reflex;
|
||
|
else if (IsEar(n0->Index, n1->Index, n2->Index, n0->Pos, n1->Pos, n2->Pos))
|
||
|
type = ImTriangulatorNodeType_Ear;
|
||
|
else
|
||
|
type = ImTriangulatorNodeType_Convex;
|
||
|
|
||
|
// Update lists when a type changes
|
||
|
if (type == n1->Type)
|
||
|
return;
|
||
|
if (n1->Type == ImTriangulatorNodeType_Reflex)
|
||
|
_Reflexes.find_erase_unsorted(n1->Index);
|
||
|
else if (n1->Type == ImTriangulatorNodeType_Ear)
|
||
|
_Ears.find_erase_unsorted(n1->Index);
|
||
|
if (type == ImTriangulatorNodeType_Reflex)
|
||
|
_Reflexes.push_back(n1);
|
||
|
else if (type == ImTriangulatorNodeType_Ear)
|
||
|
_Ears.push_back(n1);
|
||
|
n1->Type = type;
|
||
|
}
|
||
|
|
||
|
// Use ear-clipping algorithm to triangulate a simple polygon (no self-interaction, no holes).
|
||
|
// (Reminder: we don't perform any coarse clipping/culling in ImDrawList layer!
|
||
|
// It is up to caller to ensure not making costly calls that will be outside of visible area.
|
||
|
// As concave fill is noticeably more expensive than other primitives, be mindful of this...
|
||
|
// Caller can build AABB of points, and avoid filling if 'draw_list->_CmdHeader.ClipRect.Overlays(points_bb) == false')
|
||
|
void ImDrawList::AddConcavePolyFilled(const ImVec2* points, const int points_count, ImU32 col)
|
||
|
{
|
||
|
if (points_count < 3 || (col & IM_COL32_A_MASK) == 0)
|
||
|
return;
|
||
|
|
||
|
const ImVec2 uv = _Data->TexUvWhitePixel;
|
||
|
ImTriangulator triangulator;
|
||
|
unsigned int triangle[3];
|
||
|
if (Flags & ImDrawListFlags_AntiAliasedFill)
|
||
|
{
|
||
|
// Anti-aliased Fill
|
||
|
const float AA_SIZE = _FringeScale;
|
||
|
const ImU32 col_trans = col & ~IM_COL32_A_MASK;
|
||
|
const int idx_count = (points_count - 2) * 3 + points_count * 6;
|
||
|
const int vtx_count = (points_count * 2);
|
||
|
PrimReserve(idx_count, vtx_count);
|
||
|
|
||
|
// Add indexes for fill
|
||
|
unsigned int vtx_inner_idx = _VtxCurrentIdx;
|
||
|
unsigned int vtx_outer_idx = _VtxCurrentIdx + 1;
|
||
|
|
||
|
_Data->TempBuffer.reserve_discard((ImTriangulator::EstimateScratchBufferSize(points_count) + sizeof(ImVec2)) / sizeof(ImVec2));
|
||
|
triangulator.Init(points, points_count, _Data->TempBuffer.Data);
|
||
|
while (triangulator._TrianglesLeft > 0)
|
||
|
{
|
||
|
triangulator.GetNextTriangle(triangle);
|
||
|
_IdxWritePtr[0] = (ImDrawIdx)(vtx_inner_idx + (triangle[0] << 1)); _IdxWritePtr[1] = (ImDrawIdx)(vtx_inner_idx + (triangle[1] << 1)); _IdxWritePtr[2] = (ImDrawIdx)(vtx_inner_idx + (triangle[2] << 1));
|
||
|
_IdxWritePtr += 3;
|
||
|
}
|
||
|
|
||
|
// Compute normals
|
||
|
_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];
|
||
|
const ImVec2& p1 = points[i1];
|
||
|
float dx = p1.x - p0.x;
|
||
|
float dy = p1.y - p0.y;
|
||
|
IM_NORMALIZE2F_OVER_ZERO(dx, dy);
|
||
|
temp_normals[i0].x = dy;
|
||
|
temp_normals[i0].y = -dx;
|
||
|
}
|
||
|
|
||
|
for (int i0 = points_count - 1, i1 = 0; i1 < points_count; i0 = i1++)
|
||
|
{
|
||
|
// Average normals
|
||
|
const ImVec2& n0 = temp_normals[i0];
|
||
|
const ImVec2& n1 = temp_normals[i1];
|
||
|
float dm_x = (n0.x + n1.x) * 0.5f;
|
||
|
float dm_y = (n0.y + n1.y) * 0.5f;
|
||
|
IM_FIXNORMAL2F(dm_x, dm_y);
|
||
|
dm_x *= AA_SIZE * 0.5f;
|
||
|
dm_y *= AA_SIZE * 0.5f;
|
||
|
|
||
|
// Add vertices
|
||
|
_VtxWritePtr[0].pos.x = (points[i1].x - dm_x); _VtxWritePtr[0].pos.y = (points[i1].y - dm_y); _VtxWritePtr[0].uv = uv; _VtxWritePtr[0].col = col; // Inner
|
||
|
_VtxWritePtr[1].pos.x = (points[i1].x + dm_x); _VtxWritePtr[1].pos.y = (points[i1].y + dm_y); _VtxWritePtr[1].uv = uv; _VtxWritePtr[1].col = col_trans; // Outer
|
||
|
_VtxWritePtr += 2;
|
||
|
|
||
|
// Add indexes for fringes
|
||
|
_IdxWritePtr[0] = (ImDrawIdx)(vtx_inner_idx + (i1 << 1)); _IdxWritePtr[1] = (ImDrawIdx)(vtx_inner_idx + (i0 << 1)); _IdxWritePtr[2] = (ImDrawIdx)(vtx_outer_idx + (i0 << 1));
|
||
|
_IdxWritePtr[3] = (ImDrawIdx)(vtx_outer_idx + (i0 << 1)); _IdxWritePtr[4] = (ImDrawIdx)(vtx_outer_idx + (i1 << 1)); _IdxWritePtr[5] = (ImDrawIdx)(vtx_inner_idx + (i1 << 1));
|
||
|
_IdxWritePtr += 6;
|
||
|
}
|
||
|
_VtxCurrentIdx += (ImDrawIdx)vtx_count;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Non Anti-aliased Fill
|
||
|
const int idx_count = (points_count - 2) * 3;
|
||
|
const int vtx_count = points_count;
|
||
|
PrimReserve(idx_count, vtx_count);
|
||
|
for (int i = 0; i < vtx_count; i++)
|
||
|
{
|
||
|
_VtxWritePtr[0].pos = points[i]; _VtxWritePtr[0].uv = uv; _VtxWritePtr[0].col = col;
|
||
|
_VtxWritePtr++;
|
||
|
}
|
||
|
_Data->TempBuffer.reserve_discard((ImTriangulator::EstimateScratchBufferSize(points_count) + sizeof(ImVec2)) / sizeof(ImVec2));
|
||
|
triangulator.Init(points, points_count, _Data->TempBuffer.Data);
|
||
|
while (triangulator._TrianglesLeft > 0)
|
||
|
{
|
||
|
triangulator.GetNextTriangle(triangle);
|
||
|
_IdxWritePtr[0] = (ImDrawIdx)(_VtxCurrentIdx + triangle[0]); _IdxWritePtr[1] = (ImDrawIdx)(_VtxCurrentIdx + triangle[1]); _IdxWritePtr[2] = (ImDrawIdx)(_VtxCurrentIdx + triangle[2]);
|
||
|
_IdxWritePtr += 3;
|
||
|
}
|
||
|
_VtxCurrentIdx += (ImDrawIdx)vtx_count;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// [SECTION] ImDrawListSplitter
|
||
| ... | ... | |
|
if (sizeof(ImDrawIdx) == 2)
|
||
|
IM_ASSERT(draw_list->_VtxCurrentIdx < (1 << 16) && "Too many vertices in ImDrawList using 16-bit indices. Read comment above");
|
||
|
|
||
|
// Resolve callback data pointers
|
||
|
if (draw_list->_CallbacksDataBuf.Size > 0)
|
||
|
for (ImDrawCmd& cmd : draw_list->CmdBuffer)
|
||
|
if (cmd.UserCallback != NULL && cmd.UserCallbackDataOffset != -1 && cmd.UserCallbackDataSize > 0)
|
||
|
cmd.UserCallbackData = draw_list->_CallbacksDataBuf.Data + cmd.UserCallbackDataOffset;
|
||
|
|
||
|
// Add to output list + records state in ImDrawData
|
||
|
out_list->push_back(draw_list);
|
||
|
draw_data->CmdListsCount++;
|
||
| ... | ... | |
|
{
|
||
|
IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas between NewFrame() and EndFrame/Render()!");
|
||
|
IM_ASSERT(font_cfg->FontData != NULL && font_cfg->FontDataSize > 0);
|
||
|
IM_ASSERT(font_cfg->SizePixels > 0.0f);
|
||
|
IM_ASSERT(font_cfg->SizePixels > 0.0f && "Is ImFontConfig struct correctly initialized?");
|
||
|
IM_ASSERT(font_cfg->OversampleH > 0 && font_cfg->OversampleV > 0 && "Is ImFontConfig struct correctly initialized?");
|
||
|
|
||
|
// Create new font
|
||
|
if (!font_cfg->MergeMode)
|
||
|
Fonts.push_back(IM_NEW(ImFont));
|
||
|
else
|
||
|
IM_ASSERT(!Fonts.empty() && "Cannot use MergeMode for the first font"); // When using MergeMode make sure that a font has already been added before. You can use ImGui::GetIO().Fonts->AddFontDefault() to add the default imgui font.
|
||
|
IM_ASSERT(Fonts.Size > 0 && "Cannot use MergeMode for the first font"); // When using MergeMode make sure that a font has already been added before. You can use ImGui::GetIO().Fonts->AddFontDefault() to add the default imgui font.
|
||
|
|
||
|
ConfigData.push_back(*font_cfg);
|
||
|
ImFontConfig& new_font_cfg = ConfigData.back();
|
||
| ... | ... | |
|
r.Width = (unsigned short)width;
|
||
|
r.Height = (unsigned short)height;
|
||
|
r.GlyphID = id;
|
||
|
r.GlyphColored = 0; // Set to 1 manually to mark glyph as colored // FIXME: No official API for that (#8133)
|
||
|
r.GlyphAdvanceX = advance_x;
|
||
|
r.GlyphOffset = offset;
|
||
|
r.Font = font;
|
||
| ... | ... | |
|
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]);
|
||
|
// user error is to setup ImFontConfig::GlyphRanges with a pointer to data that isn't persistent,
|
||
|
// or to forget to zero-terminate the glyph range array.
|
||
|
IM_ASSERT(src_range[0] <= src_range[1] && "Invalid range: is your glyph range array persistent? it is zero-terminated?");
|
||
|
src_tmp.GlyphsHighest = ImMax(src_tmp.GlyphsHighest, (int)src_range[1]);
|
||
|
}
|
||
|
dst_tmp.SrcCount++;
|
||
| ... | ... | |
|
int unscaled_ascent, unscaled_descent, unscaled_line_gap;
|
||
|
stbtt_GetFontVMetrics(&src_tmp.FontInfo, &unscaled_ascent, &unscaled_descent, &unscaled_line_gap);
|
||
|
|
||
|
const float ascent = ImTrunc(unscaled_ascent * font_scale + ((unscaled_ascent > 0.0f) ? +1 : -1));
|
||
|
const float descent = ImTrunc(unscaled_descent * font_scale + ((unscaled_descent > 0.0f) ? +1 : -1));
|
||
|
const float ascent = ImCeil(unscaled_ascent * font_scale);
|
||
|
const float descent = ImFloor(unscaled_descent * font_scale);
|
||
|
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);
|
||
| ... | ... | |
|
ImVec2 uv0, uv1;
|
||
|
atlas->CalcCustomRectUV(r, &uv0, &uv1);
|
||
|
r->Font->AddGlyph(NULL, (ImWchar)r->GlyphID, r->GlyphOffset.x, r->GlyphOffset.y, r->GlyphOffset.x + r->Width, r->GlyphOffset.y + r->Height, uv0.x, uv0.y, uv1.x, uv1.y, r->GlyphAdvanceX);
|
||
|
if (r->GlyphColored)
|
||
|
r->Font->Glyphs.back().Colored = 1;
|
||
|
}
|
||
|
|
||
|
// Build all fonts lookup tables
|
||
| ... | ... | |
|
DirtyLookupTables = true;
|
||
|
Ascent = Descent = 0.0f;
|
||
|
MetricsTotalSurface = 0;
|
||
|
memset(Used4kPagesMap, 0, sizeof(Used4kPagesMap));
|
||
|
}
|
||
|
|
||
|
static ImWchar FindFirstExistingGlyph(ImFont* font, const ImWchar* candidate_chars, int candidate_chars_count)
|
||
| ... | ... | |
|
IndexAdvanceX[dst] = (src < index_size) ? IndexAdvanceX.Data[src] : 1.0f;
|
||
|
}
|
||
|
|
||
|
const ImFontGlyph* ImFont::FindGlyph(ImWchar c) const
|
||
|
const ImFontGlyph* ImFont::FindGlyph(ImWchar c)
|
||
|
{
|
||
|
if (c >= (size_t)IndexLookup.Size)
|
||
|
return FallbackGlyph;
|
||
| ... | ... | |
|
return &Glyphs.Data[i];
|
||
|
}
|
||
|
|
||
|
const ImFontGlyph* ImFont::FindGlyphNoFallback(ImWchar c) const
|
||
|
const ImFontGlyph* ImFont::FindGlyphNoFallback(ImWchar c)
|
||
|
{
|
||
|
if (c >= (size_t)IndexLookup.Size)
|
||
|
return NULL;
|
||
| ... | ... | |
|
return &Glyphs.Data[i];
|
||
|
}
|
||
|
|
||
|
// Wrapping skips upcoming blanks
|
||
|
// Trim trailing space and find beginning of next line
|
||
|
static inline const char* CalcWordWrapNextLineStartA(const char* text, const char* text_end)
|
||
|
{
|
||
|
while (text < text_end && ImCharIsBlankA(*text))
|
||
| ... | ... | |
|
return text;
|
||
|
}
|
||
|
|
||
|
#define ImFontGetCharAdvanceX(_FONT, _CH) ((int)(_CH) < (_FONT)->IndexAdvanceX.Size ? (_FONT)->IndexAdvanceX.Data[_CH] : (_FONT)->FallbackAdvanceX)
|
||
|
|
||
|
// 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
|
||
|
const char* ImFont::CalcWordWrapPositionA(float scale, const char* text, const char* text_end, float wrap_width)
|
||
|
{
|
||
|
// For references, possible wrap point marked with ^
|
||
|
// "aaa bbb, ccc,ddd. eee fff. ggg!"
|
||
| ... | ... | |
|
}
|
||
|
}
|
||
|
|
||
|
const float char_width = ((int)c < IndexAdvanceX.Size ? IndexAdvanceX.Data[c] : FallbackAdvanceX);
|
||
|
const float char_width = ImFontGetCharAdvanceX(this, c);
|
||
|
if (ImCharIsBlankW(c))
|
||
|
{
|
||
|
if (inside_word)
|
||
| ... | ... | |
|
return s;
|
||
|
}
|
||
|
|
||
|
ImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, const char* text_begin, const char* text_end, const char** remaining) const
|
||
|
ImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, const char* text_begin, const char* text_end, const char** remaining)
|
||
|
{
|
||
|
if (!text_end)
|
||
|
text_end = text_begin + strlen(text_begin); // FIXME-OPT: Need to avoid this.
|
||
| ... | ... | |
|
continue;
|
||
|
}
|
||
|
|
||
|
const float char_width = ((int)c < IndexAdvanceX.Size ? IndexAdvanceX.Data[c] : FallbackAdvanceX) * scale;
|
||
|
const float char_width = ImFontGetCharAdvanceX(this, c) * scale;
|
||
|
if (line_width + char_width >= max_width)
|
||
|
{
|
||
|
s = prev_s;
|
||
| ... | ... | |
|
}
|
||
|
|
||
|
// Note: as with every ImDrawList drawing function, this expects that the font atlas texture is bound.
|
||
|
void ImFont::RenderChar(ImDrawList* draw_list, float size, const ImVec2& pos, ImU32 col, ImWchar c) const
|
||
|
void ImFont::RenderChar(ImDrawList* draw_list, float size, const ImVec2& pos, ImU32 col, ImWchar c)
|
||
|
{
|
||
|
const ImFontGlyph* glyph = FindGlyph(c);
|
||
|
if (!glyph || !glyph->Visible)
|
||
| ... | ... | |
|
}
|
||
|
|
||
|
// Note: as with every ImDrawList drawing function, this expects that the font atlas texture is bound.
|
||
|
void ImFont::RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, ImU32 col, const ImVec4& clip_rect, const char* text_begin, const char* text_end, float wrap_width, bool cpu_fine_clip) const
|
||
|
void ImFont::RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, ImU32 col, const ImVec4& clip_rect, const char* text_begin, const char* text_end, float wrap_width, bool cpu_fine_clip)
|
||
|
{
|
||
|
if (!text_end)
|
||
|
text_end = text_begin + strlen(text_begin); // ImGui:: functions generally already provides a valid text_end, so this is merely to handle direct calls.
|
||
| ... | ... | |
|
if (y > clip_rect.w)
|
||
|
return;
|
||
|
|
||
|
const float start_x = x;
|
||
|
const float scale = size / FontSize;
|
||
|
const float line_height = FontSize * scale;
|
||
|
const float origin_x = x;
|
||
|
const bool word_wrap_enabled = (wrap_width > 0.0f);
|
||
|
|
||
|
// Fast-forward to first visible line
|
||
| ... | ... | |
|
{
|
||
|
// 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));
|
||
|
word_wrap_eol = CalcWordWrapPositionA(scale, s, text_end, wrap_width - (x - origin_x));
|
||
|
|
||
|
if (s >= word_wrap_eol)
|
||
|
{
|
||
|
x = start_x;
|
||
|
x = origin_x;
|
||
|
y += line_height;
|
||
|
if (y > clip_rect.w)
|
||
|
break; // break out of main loop
|
||
|
word_wrap_eol = NULL;
|
||
|
s = CalcWordWrapNextLineStartA(s, text_end); // Wrapping skips upcoming blanks
|
||
|
continue;
|
||
| ... | ... | |
|
{
|
||
|
if (c == '\n')
|
||
|
{
|
||
|
x = start_x;
|
||
|
x = origin_x;
|
||
|
y += line_height;
|
||
|
if (y > clip_rect.w)
|
||
|
break; // break out of main loop
|
||
| ... | ... | |
|
}
|
||
|
else
|
||
|
{
|
||
|
draw_list->PathArcTo(ImVec2(x0, p1.y - rounding), rounding, IM_PI - arc0_e, IM_PI - arc0_b, 3); // BL
|
||
|
draw_list->PathArcTo(ImVec2(x0, p0.y + rounding), rounding, IM_PI + arc0_b, IM_PI + arc0_e, 3); // TR
|
||
|
draw_list->PathArcTo(ImVec2(x0, p1.y - rounding), rounding, IM_PI - arc0_e, IM_PI - arc0_b); // BL
|
||
|
draw_list->PathArcTo(ImVec2(x0, p0.y + rounding), rounding, IM_PI + arc0_b, IM_PI + arc0_e); // TR
|
||
|
}
|
||
|
if (p1.x > rect.Min.x + rounding)
|
||
|
{
|
||
| ... | ... | |
|
}
|
||
|
else
|
||
|
{
|
||
|
draw_list->PathArcTo(ImVec2(x1, p0.y + rounding), rounding, -arc1_e, -arc1_b, 3); // TR
|
||
|
draw_list->PathArcTo(ImVec2(x1, p1.y - rounding), rounding, +arc1_b, +arc1_e, 3); // BR
|
||
|
draw_list->PathArcTo(ImVec2(x1, p0.y + rounding), rounding, -arc1_e, -arc1_b); // TR
|
||
|
draw_list->PathArcTo(ImVec2(x1, p1.y - rounding), rounding, +arc1_b, +arc1_e); // BR
|
||
|
}
|
||
|
}
|
||
|
draw_list->PathFillConvex(col);
|
||
Update Dear ImGui to v1.91.5.