Project

General

Profile

« Previous | Next » 

Revision a8009d0e

Added by david.sorber 8 months ago

Update Dear ImGui to version 1.92.5.

View differences:

external/imgui/imgui_draw.cpp
// dear imgui, v1.91.5
// dear imgui, v1.92.5
// (drawing and font code)
/*
......
// [SECTION] ImDrawData
// [SECTION] Helpers ShadeVertsXXX functions
// [SECTION] ImFontConfig
// [SECTION] ImFontAtlas
// [SECTION] ImFontAtlas glyph ranges helpers
// [SECTION] ImFontAtlas, ImFontAtlasBuilder
// [SECTION] ImFontAtlas: backend for stb_truetype
// [SECTION] ImFontAtlas: glyph ranges helpers
// [SECTION] ImFontGlyphRangesBuilder
// [SECTION] ImFont
// [SECTION] ImGui Internal Render Helpers
......
#endif
#include <stdio.h> // vsnprintf, sscanf, printf
#include <stdint.h> // intptr_t
// Visual Studio warnings
#ifdef _MSC_VER
......
#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: 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)
#pragma warning (disable: 26812) // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3).
#endif
// Clang/GCC warnings with -Weverything
......
#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
#pragma clang diagnostic ignored "-Wcast-qual" // warning: cast from 'const xxxx *' to 'xxx *' drops const qualifier
#pragma clang diagnostic ignored "-Wswitch-default" // warning: 'switch' missing 'default' label
#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 "-Wfloat-equal" // warning: comparing floating-point with '==' or '!=' is unsafe
#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 "-Wstrict-overflow" // warning: assuming signed overflow does not occur when simplifying division / ..when changing X +- C1 cmp C2 to X cmp C2 -+ C1
#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 "-Wcast-qual" // warning: cast from type 'const xxxx *' to type 'xxxx *' casts away qualifiers
#endif
//-------------------------------------------------------------------------
......
#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
#endif
#if defined(__GNUC__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wtype-limits" // warning: comparison is always true due to limited range of data type [-Wtype-limits]
#pragma GCC diagnostic ignored "-Wcast-qual" // warning: cast from type 'const xxxx *' to type 'xxxx *' casts away qualifiers
#pragma GCC diagnostic ignored "-Wimplicit-fallthrough" // warning: this statement may fall through
#endif
#ifndef STB_RECT_PACK_IMPLEMENTATION // in case the user already have an implementation in the _same_ compilation unit (e.g. unity builds)
......
#define STBTT_fabs(x) ImFabs(x)
#define STBTT_ifloor(x) ((int)ImFloor(x))
#define STBTT_iceil(x) ((int)ImCeil(x))
#define STBTT_strlen(x) ImStrlen(x)
#define STBTT_STATIC
#define STB_TRUETYPE_IMPLEMENTATION
#else
......
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_InputTextCursor] = colors[ImGuiCol_Text];
colors[ImGuiCol_TabHovered] = colors[ImGuiCol_HeaderHovered];
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_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_TreeLines] = colors[ImGuiCol_Border];
colors[ImGuiCol_DragDropTarget] = ImVec4(1.00f, 1.00f, 0.00f, 0.90f);
colors[ImGuiCol_DragDropTargetBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
colors[ImGuiCol_UnsavedMarker] = ImVec4(1.00f, 1.00f, 1.00f, 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_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_InputTextCursor] = colors[ImGuiCol_Text];
colors[ImGuiCol_TabHovered] = colors[ImGuiCol_HeaderHovered];
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_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_TreeLines] = colors[ImGuiCol_Border];
colors[ImGuiCol_DragDropTarget] = ImVec4(1.00f, 1.00f, 0.00f, 0.90f);
colors[ImGuiCol_DragDropTargetBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
colors[ImGuiCol_UnsavedMarker] = ImVec4(0.90f, 0.90f, 0.90f, 1.00f);
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_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_InputTextCursor] = colors[ImGuiCol_Text];
colors[ImGuiCol_TabHovered] = colors[ImGuiCol_HeaderHovered];
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_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_TreeLines] = colors[ImGuiCol_Border];
colors[ImGuiCol_DragDropTarget] = ImVec4(0.26f, 0.59f, 0.98f, 0.95f);
colors[ImGuiCol_DragDropTargetBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
colors[ImGuiCol_UnsavedMarker] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f);
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);
......
ImDrawListSharedData::ImDrawListSharedData()
{
memset(this, 0, sizeof(*this));
InitialFringeScale = 1.0f;
for (int i = 0; i < IM_ARRAYSIZE(ArcFastVtx); i++)
{
const float a = ((float)i * 2 * IM_PI) / (float)IM_ARRAYSIZE(ArcFastVtx);
......
ArcFastRadiusCutoff = IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC_R(IM_DRAWLIST_ARCFAST_SAMPLE_MAX, CircleSegmentMaxError);
}
ImDrawListSharedData::~ImDrawListSharedData()
{
IM_ASSERT(DrawLists.Size == 0);
}
void ImDrawListSharedData::SetCircleTessellationMaxError(float max_error)
{
if (CircleSegmentMaxError == max_error)
......
ArcFastRadiusCutoff = IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC_R(IM_DRAWLIST_ARCFAST_SAMPLE_MAX, CircleSegmentMaxError);
}
ImDrawList::ImDrawList(ImDrawListSharedData* shared_data)
{
memset(this, 0, sizeof(*this));
_SetDrawListSharedData(shared_data);
}
ImDrawList::~ImDrawList()
{
_ClearFreeMemory();
_SetDrawListSharedData(NULL);
}
void ImDrawList::_SetDrawListSharedData(ImDrawListSharedData* data)
{
if (_Data != NULL)
_Data->DrawLists.find_erase_unsorted(this);
_Data = data;
if (_Data != NULL)
_Data->DrawLists.push_back(this);
}
// 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.
// In the majority of cases, you would want to call PushClipRect() and PushTexture() after this.
void ImDrawList::_ResetForNewFrame()
{
// Verify that the ImDrawCmd fields we want to memcmp() are contiguous in memory.
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));
IM_STATIC_ASSERT(offsetof(ImDrawCmd, TexRef) == sizeof(ImVec4));
IM_STATIC_ASSERT(offsetof(ImDrawCmd, VtxOffset) == sizeof(ImVec4) + sizeof(ImTextureRef));
if (_Splitter._Count > 1)
_Splitter.Merge(this);
......
_VtxWritePtr = NULL;
_IdxWritePtr = NULL;
_ClipRectStack.resize(0);
_TextureIdStack.resize(0);
_TextureStack.resize(0);
_CallbacksDataBuf.resize(0);
_Path.resize(0);
_Splitter.Clear();
CmdBuffer.push_back(ImDrawCmd());
_FringeScale = 1.0f;
_FringeScale = _Data->InitialFringeScale;
}
void ImDrawList::_ClearFreeMemory()
......
_VtxWritePtr = NULL;
_IdxWritePtr = NULL;
_ClipRectStack.clear();
_TextureIdStack.clear();
_TextureStack.clear();
_CallbacksDataBuf.clear();
_Path.clear();
_Splitter.ClearFreeMemory();
}
// Note: For multi-threaded rendering, consider using `imgui_threaded_rendering` from https://github.com/ocornut/imgui_club
ImDrawList* ImDrawList::CloneOutput() const
{
ImDrawList* dst = IM_NEW(ImDrawList(_Data));
ImDrawList* dst = IM_NEW(ImDrawList(NULL));
dst->CmdBuffer = CmdBuffer;
dst->IdxBuffer = IdxBuffer;
dst->VtxBuffer = VtxBuffer;
......
{
ImDrawCmd draw_cmd;
draw_cmd.ClipRect = _CmdHeader.ClipRect; // Same as calling ImDrawCmd_HeaderCopy()
draw_cmd.TextureId = _CmdHeader.TextureId;
draw_cmd.TexRef = _CmdHeader.TexRef;
draw_cmd.VtxOffset = _CmdHeader.VtxOffset;
draw_cmd.IdxOffset = IdxBuffer.Size;
......
{
IM_ASSERT_PARANOID(CmdBuffer.Size > 0);
ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1];
IM_ASSERT(callback != NULL);
IM_ASSERT(curr_cmd->UserCallback == NULL);
if (curr_cmd->ElemCount != 0)
{
......
AddDrawCmd(); // Force a new command after us (see comment below)
}
// Compare ClipRect, TextureId and VtxOffset with a single memcmp()
// Compare ClipRect, TexRef and VtxOffset with a single memcmp()
#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_HeaderCompare(CMD_LHS, CMD_RHS) (memcmp(CMD_LHS, CMD_RHS, ImDrawCmd_HeaderSize)) // Compare ClipRect, TexRef, VtxOffset
#define ImDrawCmd_HeaderCopy(CMD_DST, CMD_SRC) (memcpy(CMD_DST, CMD_SRC, ImDrawCmd_HeaderSize)) // Copy ClipRect, TexRef, VtxOffset
#define ImDrawCmd_AreSequentialIdxOffset(CMD_0, CMD_1) (CMD_0->IdxOffset + CMD_0->ElemCount == CMD_1->IdxOffset)
// Try to merge two last draw commands
......
curr_cmd->ClipRect = _CmdHeader.ClipRect;
}
void ImDrawList::_OnChangedTextureID()
void ImDrawList::_OnChangedTexture()
{
// If current command is used with different settings we need to add a new command
IM_ASSERT_PARANOID(CmdBuffer.Size > 0);
ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1];
if (curr_cmd->ElemCount != 0 && curr_cmd->TextureId != _CmdHeader.TextureId)
if (curr_cmd->ElemCount != 0 && curr_cmd->TexRef != _CmdHeader.TexRef)
{
AddDrawCmd();
return;
}
IM_ASSERT(curr_cmd->UserCallback == NULL);
// Unlike other _OnChangedXXX functions this may be called by ImFontAtlasUpdateDrawListsTextures() in more locations so we need to handle this case.
if (curr_cmd->UserCallback != NULL)
return;
// Try to merge with previous command if it matches, else use current command
ImDrawCmd* prev_cmd = curr_cmd - 1;
......
CmdBuffer.pop_back();
return;
}
curr_cmd->TextureId = _CmdHeader.TextureId;
curr_cmd->TexRef = _CmdHeader.TexRef;
}
void ImDrawList::_OnChangedVtxOffset()
......
_OnChangedClipRect();
}
void ImDrawList::PushTextureID(ImTextureID texture_id)
void ImDrawList::PushTexture(ImTextureRef tex_ref)
{
_TextureIdStack.push_back(texture_id);
_CmdHeader.TextureId = texture_id;
_OnChangedTextureID();
_TextureStack.push_back(tex_ref);
_CmdHeader.TexRef = tex_ref;
if (tex_ref._TexData != NULL)
IM_ASSERT(tex_ref._TexData->WantDestroyNextFrame == false);
_OnChangedTexture();
}
void ImDrawList::PopTextureID()
void ImDrawList::PopTexture()
{
_TextureIdStack.pop_back();
_CmdHeader.TextureId = (_TextureIdStack.Size == 0) ? (ImTextureID)NULL : _TextureIdStack.Data[_TextureIdStack.Size - 1];
_OnChangedTextureID();
_TextureStack.pop_back();
_CmdHeader.TexRef = (_TextureStack.Size == 0) ? ImTextureRef() : _TextureStack.Data[_TextureStack.Size - 1];
_OnChangedTexture();
}
// 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)
// This is used by ImGui::PushFont()/PopFont(). It works because we never use _TextureIdStack[] elsewhere than in PushTexture()/PopTexture().
void ImDrawList::_SetTexture(ImTextureRef tex_ref)
{
if (_CmdHeader.TextureId == texture_id)
if (_CmdHeader.TexRef == tex_ref)
return;
_CmdHeader.TextureId = texture_id;
_OnChangedTextureID();
_CmdHeader.TexRef = tex_ref;
_TextureStack.back() = tex_ref;
_OnChangedTexture();
}
// Reserve space for a number of vertices and indices.
......
const bool use_texture = (Flags & ImDrawListFlags_AntiAliasedLinesUseTex) && (integer_thickness < IM_DRAWLIST_TEX_LINES_WIDTH_MAX) && (fractional_thickness <= 0.00001f) && (AA_SIZE == 1.0f);
// We should never hit this, because NewFrame() doesn't set ImDrawListFlags_AntiAliasedLinesUseTex unless ImFontAtlasFlags_NoBakedLines is off
IM_ASSERT_PARANOID(!use_texture || !(_Data->Font->ContainerAtlas->Flags & ImFontAtlasFlags_NoBakedLines));
IM_ASSERT_PARANOID(!use_texture || !(_Data->Font->OwnerAtlas->Flags & ImFontAtlasFlags_NoBakedLines));
const int idx_count = use_texture ? (count * 6) : (thick_line ? count * 18 : count * 12);
const int vtx_count = use_texture ? (points_count * 2) : (thick_line ? points_count * 4 : points_count * 3);
......
dm_x *= half_draw_size; // dm_x, dm_y are offset to the outer edge of the AA area
dm_y *= half_draw_size;
// Add temporary vertexes for the outer edges
// Add temporary vertices for the outer edges
ImVec2* out_vtx = &temp_points[i2 * 2];
out_vtx[0].x = points[i2].x + dm_x;
out_vtx[0].y = points[i2].y + dm_y;
......
idx1 = idx2;
}
// Add vertexes for each point on the line
// Add vertices for each point on the line
if (use_texture)
{
// If we're using textures we only need to emit the left/right edge vertices
......
// Accept null ranges
if (text_begin == text_end || text_begin[0] == 0)
return;
if (text_end == NULL)
text_end = text_begin + strlen(text_begin);
// No need to strlen() here: font->RenderText() will do it and may early out.
// Pull default font/size from the shared ImDrawListSharedData instance
if (font == NULL)
......
if (font_size == 0.0f)
font_size = _Data->FontSize;
IM_ASSERT(font->ContainerAtlas->TexID == _CmdHeader.TextureId); // Use high-level ImGui::PushFont() or low-level ImDrawList::PushTextureId() to change font.
ImVec4 clip_rect = _CmdHeader.ClipRect;
if (cpu_fine_clip_rect)
{
......
clip_rect.z = ImMin(clip_rect.z, cpu_fine_clip_rect->z);
clip_rect.w = ImMin(clip_rect.w, cpu_fine_clip_rect->w);
}
font->RenderText(this, font_size, pos, col, clip_rect, text_begin, text_end, wrap_width, cpu_fine_clip_rect != NULL);
font->RenderText(this, font_size, pos, col, clip_rect, text_begin, text_end, wrap_width, (cpu_fine_clip_rect != NULL) ? ImDrawTextFlags_CpuFineClip : ImDrawTextFlags_None);
}
void ImDrawList::AddText(const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end)
{
AddText(NULL, 0.0f, pos, col, text_begin, text_end);
AddText(_Data->Font, _Data->FontSize, pos, col, text_begin, text_end);
}
void ImDrawList::AddImage(ImTextureID user_texture_id, const ImVec2& p_min, const ImVec2& p_max, const ImVec2& uv_min, const ImVec2& uv_max, ImU32 col)
void ImDrawList::AddImage(ImTextureRef tex_ref, const ImVec2& p_min, const ImVec2& p_max, const ImVec2& uv_min, const ImVec2& uv_max, ImU32 col)
{
if ((col & IM_COL32_A_MASK) == 0)
return;
const bool push_texture_id = user_texture_id != _CmdHeader.TextureId;
const bool push_texture_id = tex_ref != _CmdHeader.TexRef;
if (push_texture_id)
PushTextureID(user_texture_id);
PushTexture(tex_ref);
PrimReserve(6, 4);
PrimRectUV(p_min, p_max, uv_min, uv_max, col);
if (push_texture_id)
PopTextureID();
PopTexture();
}
void ImDrawList::AddImageQuad(ImTextureID user_texture_id, const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& uv1, const ImVec2& uv2, const ImVec2& uv3, const ImVec2& uv4, ImU32 col)
void ImDrawList::AddImageQuad(ImTextureRef tex_ref, const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& uv1, const ImVec2& uv2, const ImVec2& uv3, const ImVec2& uv4, ImU32 col)
{
if ((col & IM_COL32_A_MASK) == 0)
return;
const bool push_texture_id = user_texture_id != _CmdHeader.TextureId;
const bool push_texture_id = tex_ref != _CmdHeader.TexRef;
if (push_texture_id)
PushTextureID(user_texture_id);
PushTexture(tex_ref);
PrimReserve(6, 4);
PrimQuadUV(p1, p2, p3, p4, uv1, uv2, uv3, uv4, col);
if (push_texture_id)
PopTextureID();
PopTexture();
}
void ImDrawList::AddImageRounded(ImTextureID user_texture_id, const ImVec2& p_min, const ImVec2& p_max, const ImVec2& uv_min, const ImVec2& uv_max, ImU32 col, float rounding, ImDrawFlags flags)
void ImDrawList::AddImageRounded(ImTextureRef tex_ref, const ImVec2& p_min, const ImVec2& p_max, const ImVec2& uv_min, const ImVec2& uv_max, ImU32 col, float rounding, ImDrawFlags flags)
{
if ((col & IM_COL32_A_MASK) == 0)
return;
......
flags = FixRectCornerFlags(flags);
if (rounding < 0.5f || (flags & ImDrawFlags_RoundCornersMask_) == ImDrawFlags_RoundCornersNone)
{
AddImage(user_texture_id, p_min, p_max, uv_min, uv_max, col);
AddImage(tex_ref, p_min, p_max, uv_min, uv_max, col);
return;
}
const bool push_texture_id = user_texture_id != _CmdHeader.TextureId;
const bool push_texture_id = tex_ref != _CmdHeader.TexRef;
if (push_texture_id)
PushTextureID(user_texture_id);
PushTexture(tex_ref);
int vert_start_idx = VtxBuffer.Size;
PathRect(p_min, p_max, rounding, flags);
......
ImGui::ShadeVertsLinearUV(this, vert_start_idx, vert_end_idx, p_min, p_max, uv_min, uv_max, true);
if (push_texture_id)
PopTextureID();
PopTexture();
}
//-----------------------------------------------------------------------------
......
// If current command is used with different settings we need to add a new command
ImDrawCmd* curr_cmd = &draw_list->CmdBuffer.Data[draw_list->CmdBuffer.Size - 1];
if (curr_cmd->ElemCount == 0)
ImDrawCmd_HeaderCopy(curr_cmd, &draw_list->_CmdHeader); // Copy ClipRect, TextureId, VtxOffset
ImDrawCmd_HeaderCopy(curr_cmd, &draw_list->_CmdHeader); // Copy ClipRect, TexRef, VtxOffset
else if (ImDrawCmd_HeaderCompare(curr_cmd, &draw_list->_CmdHeader) != 0)
draw_list->AddDrawCmd();
......
if (curr_cmd == NULL)
draw_list->AddDrawCmd();
else if (curr_cmd->ElemCount == 0)
ImDrawCmd_HeaderCopy(curr_cmd, &draw_list->_CmdHeader); // Copy ClipRect, TextureId, VtxOffset
ImDrawCmd_HeaderCopy(curr_cmd, &draw_list->_CmdHeader); // Copy ClipRect, TexRef, VtxOffset
else if (ImDrawCmd_HeaderCompare(curr_cmd, &draw_list->_CmdHeader) != 0)
draw_list->AddDrawCmd();
}
......
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;
Textures = NULL;
}
// Important: 'out_list' is generally going to be draw_data->CmdLists, but may be another temporary list
......
{
ImVector<ImDrawVert> new_vtx_buffer;
TotalVtxCount = TotalIdxCount = 0;
for (int i = 0; i < CmdListsCount; i++)
for (ImDrawList* draw_list : CmdLists)
{
ImDrawList* cmd_list = CmdLists[i];
if (cmd_list->IdxBuffer.empty())
if (draw_list->IdxBuffer.empty())
continue;
new_vtx_buffer.resize(cmd_list->IdxBuffer.Size);
for (int j = 0; j < cmd_list->IdxBuffer.Size; j++)
new_vtx_buffer[j] = cmd_list->VtxBuffer[cmd_list->IdxBuffer[j]];
cmd_list->VtxBuffer.swap(new_vtx_buffer);
cmd_list->IdxBuffer.resize(0);
TotalVtxCount += cmd_list->VtxBuffer.Size;
new_vtx_buffer.resize(draw_list->IdxBuffer.Size);
for (int j = 0; j < draw_list->IdxBuffer.Size; j++)
new_vtx_buffer[j] = draw_list->VtxBuffer[draw_list->IdxBuffer[j]];
draw_list->VtxBuffer.swap(new_vtx_buffer);
draw_list->IdxBuffer.resize(0);
TotalVtxCount += draw_list->VtxBuffer.Size;
}
}
......
// [SECTION] ImFontConfig
//-----------------------------------------------------------------------------
// FIXME-NEWATLAS: Oversample specification could be more dynamic. For now, favoring automatic selection.
ImFontConfig::ImFontConfig()
{
memset(this, 0, sizeof(*this));
FontDataOwnedByAtlas = true;
OversampleH = 2;
OversampleV = 1;
OversampleH = 0; // Auto == 1 or 2 depending on size
OversampleV = 0; // Auto == 1
GlyphMaxAdvanceX = FLT_MAX;
RasterizerMultiply = 1.0f;
RasterizerDensity = 1.0f;
EllipsisChar = (ImWchar)-1;
EllipsisChar = 0;
}
//-----------------------------------------------------------------------------
// [SECTION] ImTextureData
//-----------------------------------------------------------------------------
// - ImTextureData::Create()
// - ImTextureData::DestroyPixels()
//-----------------------------------------------------------------------------
int ImTextureDataGetFormatBytesPerPixel(ImTextureFormat format)
{
switch (format)
{
case ImTextureFormat_Alpha8: return 1;
case ImTextureFormat_RGBA32: return 4;
}
IM_ASSERT(0);
return 0;
}
const char* ImTextureDataGetStatusName(ImTextureStatus status)
{
switch (status)
{
case ImTextureStatus_OK: return "OK";
case ImTextureStatus_Destroyed: return "Destroyed";
case ImTextureStatus_WantCreate: return "WantCreate";
case ImTextureStatus_WantUpdates: return "WantUpdates";
case ImTextureStatus_WantDestroy: return "WantDestroy";
}
return "N/A";
}
const char* ImTextureDataGetFormatName(ImTextureFormat format)
{
switch (format)
{
case ImTextureFormat_Alpha8: return "Alpha8";
case ImTextureFormat_RGBA32: return "RGBA32";
}
return "N/A";
}
void ImTextureData::Create(ImTextureFormat format, int w, int h)
{
IM_ASSERT(Status == ImTextureStatus_Destroyed);
DestroyPixels();
Format = format;
Status = ImTextureStatus_WantCreate;
Width = w;
Height = h;
BytesPerPixel = ImTextureDataGetFormatBytesPerPixel(format);
UseColors = false;
Pixels = (unsigned char*)IM_ALLOC(Width * Height * BytesPerPixel);
IM_ASSERT(Pixels != NULL);
memset(Pixels, 0, Width * Height * BytesPerPixel);
UsedRect.x = UsedRect.y = UsedRect.w = UsedRect.h = 0;
UpdateRect.x = UpdateRect.y = (unsigned short)~0;
UpdateRect.w = UpdateRect.h = 0;
}
void ImTextureData::DestroyPixels()
{
if (Pixels)
IM_FREE(Pixels);
Pixels = NULL;
UseColors = false;
}
//-----------------------------------------------------------------------------
// [SECTION] ImFontAtlas
// [SECTION] ImFontAtlas, ImFontAtlasBuilder
//-----------------------------------------------------------------------------
// - Default texture data encoded in ASCII
// - ImFontAtlas()
// - ImFontAtlas::Clear()
// - ImFontAtlas::CompactCache()
// - ImFontAtlas::ClearInputData()
// - ImFontAtlas::ClearTexData()
// - ImFontAtlas::ClearFonts()
//-----------------------------------------------------------------------------
// - ImFontAtlasUpdateNewFrame()
// - ImFontAtlasTextureBlockConvert()
// - ImFontAtlasTextureBlockPostProcess()
// - ImFontAtlasTextureBlockPostProcessMultiply()
// - ImFontAtlasTextureBlockFill()
// - ImFontAtlasTextureBlockCopy()
// - ImFontAtlasTextureBlockQueueUpload()
//-----------------------------------------------------------------------------
// - ImFontAtlas::GetTexDataAsAlpha8() [legacy]
// - ImFontAtlas::GetTexDataAsRGBA32() [legacy]
// - ImFontAtlas::Build() [legacy]
//-----------------------------------------------------------------------------
// - ImFontAtlas::AddFont()
// - ImFontAtlas::AddFontDefault()
// - ImFontAtlas::AddFontFromFileTTF()
// - ImFontAtlas::AddFontFromMemoryTTF()
// - ImFontAtlas::AddFontFromMemoryCompressedTTF()
// - ImFontAtlas::AddFontFromMemoryCompressedBase85TTF()
// - ImFontAtlas::RemoveFont()
// - ImFontAtlasBuildNotifySetFont()
//-----------------------------------------------------------------------------
// - ImFontAtlas::AddCustomRect()
// - ImFontAtlas::RemoveCustomRect()
// - ImFontAtlas::GetCustomRect()
// - ImFontAtlas::AddCustomRectFontGlyph() [legacy]
// - ImFontAtlas::AddCustomRectFontGlyphForSize() [legacy]
// - ImFontAtlasGetMouseCursorTexData()
//-----------------------------------------------------------------------------
// - ImFontAtlasBuildMain()
// - ImFontAtlasBuildSetupFontLoader()
// - ImFontAtlasBuildPreloadAllGlyphRanges()
// - ImFontAtlasBuildUpdatePointers()
// - ImFontAtlasBuildRenderBitmapFromString()
// - ImFontAtlasBuildUpdateBasicTexData()
// - ImFontAtlasBuildUpdateLinesTexData()
// - ImFontAtlasBuildAddFont()
// - ImFontAtlasBuildSetupFontBakedEllipsis()
// - ImFontAtlasBuildSetupFontBakedBlanks()
// - ImFontAtlasBuildSetupFontBakedFallback()
// - ImFontAtlasBuildSetupFontSpecialGlyphs()
// - ImFontAtlasBuildDiscardBakes()
// - ImFontAtlasBuildDiscardFontBakedGlyph()
// - ImFontAtlasBuildDiscardFontBaked()
// - ImFontAtlasBuildDiscardFontBakes()
//-----------------------------------------------------------------------------
// - ImFontAtlasAddDrawListSharedData()
// - ImFontAtlasRemoveDrawListSharedData()
// - ImFontAtlasUpdateDrawListsTextures()
// - ImFontAtlasUpdateDrawListsSharedData()
//-----------------------------------------------------------------------------
// - ImFontAtlasBuildSetTexture()
// - ImFontAtlasBuildAddTexture()
// - ImFontAtlasBuildMakeSpace()
// - ImFontAtlasBuildRepackTexture()
// - ImFontAtlasBuildGrowTexture()
// - ImFontAtlasBuildRepackOrGrowTexture()
// - ImFontAtlasBuildGetTextureSizeEstimate()
// - ImFontAtlasBuildCompactTexture()
// - ImFontAtlasBuildInit()
// - ImFontAtlasBuildDestroy()
//-----------------------------------------------------------------------------
// - ImFontAtlasPackInit()
// - ImFontAtlasPackAllocRectEntry()
// - ImFontAtlasPackReuseRectEntry()
// - ImFontAtlasPackDiscardRect()
// - ImFontAtlasPackAddRect()
// - ImFontAtlasPackGetRect()
//-----------------------------------------------------------------------------
// - ImFontBaked_BuildGrowIndex()
// - ImFontBaked_BuildLoadGlyph()
// - ImFontBaked_BuildLoadGlyphAdvanceX()
// - ImFontAtlasDebugLogTextureRequests()
//-----------------------------------------------------------------------------
// - ImFontAtlasGetFontLoaderForStbTruetype()
//-----------------------------------------------------------------------------
// A work of art lies ahead! (. = white layer, X = black layer, others are blank)
......
{ ImVec2(73,0), ImVec2(17,17), ImVec2( 8, 8) }, // ImGuiMouseCursor_ResizeNESW
{ ImVec2(55,0), ImVec2(17,17), ImVec2( 8, 8) }, // ImGuiMouseCursor_ResizeNWSE
{ ImVec2(91,0), ImVec2(17,22), ImVec2( 5, 0) }, // ImGuiMouseCursor_Hand
{ ImVec2(0,3), ImVec2(12,19), ImVec2(0, 0) }, // ImGuiMouseCursor_Wait // Arrow + custom code in ImGui::RenderMouseCursor()
{ ImVec2(0,3), ImVec2(12,19), ImVec2(0, 0) }, // ImGuiMouseCursor_Progress // Arrow + custom code in ImGui::RenderMouseCursor()
{ ImVec2(109,0),ImVec2(13,15), ImVec2( 6, 7) }, // ImGuiMouseCursor_NotAllowed
};
#define IM_FONTGLYPH_INDEX_UNUSED ((ImU16)-1) // 0xFFFF
#define IM_FONTGLYPH_INDEX_NOT_FOUND ((ImU16)-2) // 0xFFFE
ImFontAtlas::ImFontAtlas()
{
memset(this, 0, sizeof(*this));
TexDesiredFormat = ImTextureFormat_RGBA32;
TexGlyphPadding = 1;
PackIdMouseCursors = PackIdLines = -1;
TexMinWidth = 512;
TexMinHeight = 128;
TexMaxWidth = 8192;
TexMaxHeight = 8192;
TexRef._TexID = ImTextureID_Invalid;
RendererHasTextures = false; // Assumed false by default, as apps can call e.g Atlas::Build() after backend init and before ImGui can update.
TexNextUniqueID = 1;
FontNextUniqueID = 1;
Builder = NULL;
}
ImFontAtlas::~ImFontAtlas()
{
IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas between NewFrame() and EndFrame/Render()!");
Clear();
IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas!");
RendererHasTextures = false; // Full Clear() is supported, but ClearTexData() only isn't.
ClearFonts();
ClearTexData();
TexList.clear_delete();
TexData = NULL;
}
void ImFontAtlas::ClearInputData()
// If you call this mid-frame, you would need to add new font and bind them!
void ImFontAtlas::Clear()
{
IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas between NewFrame() and EndFrame/Render()!");
for (ImFontConfig& font_cfg : ConfigData)
if (font_cfg.FontData && font_cfg.FontDataOwnedByAtlas)
{
IM_FREE(font_cfg.FontData);
font_cfg.FontData = NULL;
}
bool backup_renderer_has_textures = RendererHasTextures;
RendererHasTextures = false; // Full Clear() is supported, but ClearTexData() only isn't.
ClearFonts();
ClearTexData();
RendererHasTextures = backup_renderer_has_textures;
}
void ImFontAtlas::CompactCache()
{
ImFontAtlasTextureCompact(this);
}
void ImFontAtlas::SetFontLoader(const ImFontLoader* font_loader)
{
ImFontAtlasBuildSetupFontLoader(this, font_loader);
}
void ImFontAtlas::ClearInputData()
{
IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas!");
for (ImFont* font : Fonts)
ImFontAtlasFontDestroyOutput(this, font);
for (ImFontConfig& font_cfg : Sources)
ImFontAtlasFontDestroySourceData(this, &font_cfg);
for (ImFont* font : Fonts)
{
// When clearing this we lose access to the font name and other information used to build the font.
font->Sources.clear();
font->Flags |= ImFontFlags_NoLoadGlyphs;
}
Sources.clear();
}
// Clear CPU-side copy of the texture data.
void ImFontAtlas::ClearTexData()
{
IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas!");
IM_ASSERT(RendererHasTextures == false && "Not supported for dynamic atlases, but you may call Clear().");
for (ImTextureData* tex : TexList)
tex->DestroyPixels();
//Locked = true; // Hoped to be able to lock this down but some reload patterns may not be happy with it.
}
// When clearing this we lose access to the font name and other information used to build the font.
void ImFontAtlas::ClearFonts()
{
// FIXME-NEWATLAS: Illegal to remove currently bound font.
IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas!");
for (ImFont* font : Fonts)
if (font->ConfigData >= ConfigData.Data && font->ConfigData < ConfigData.Data + ConfigData.Size)
ImFontAtlasBuildNotifySetFont(this, font, NULL);
ImFontAtlasBuildDestroy(this);
ClearInputData();
Fonts.clear_delete();
TexIsBuilt = false;
for (ImDrawListSharedData* shared_data : DrawListSharedDatas)
if (shared_data->FontAtlas == this)
{
font->ConfigData = NULL;
font->ConfigDataCount = 0;
shared_data->Font = NULL;
shared_data->FontScale = shared_data->FontSize = 0.0f;
}
ConfigData.clear();
CustomRects.clear();
PackIdMouseCursors = PackIdLines = -1;
// Important: we leave TexReady untouched
}
void ImFontAtlas::ClearTexData()
static void ImFontAtlasBuildUpdateRendererHasTexturesFromContext(ImFontAtlas* atlas)
{
IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas between NewFrame() and EndFrame/Render()!");
if (TexPixelsAlpha8)
IM_FREE(TexPixelsAlpha8);
if (TexPixelsRGBA32)
IM_FREE(TexPixelsRGBA32);
TexPixelsAlpha8 = NULL;
TexPixelsRGBA32 = NULL;
TexPixelsUseColors = false;
// Important: we leave TexReady untouched
// [LEGACY] Copy back the ImGuiBackendFlags_RendererHasTextures flag from ImGui context.
// - This is the 1% exceptional case where that dependency if useful, to bypass an issue where otherwise at the
// time of an early call to Build(), it would be impossible for us to tell if the backend supports texture update.
// - Without this hack, we would have quite a pitfall as many legacy codebases have an early call to Build().
// Whereas conversely, the portion of people using ImDrawList without ImGui is expected to be pathologically rare.
for (ImDrawListSharedData* shared_data : atlas->DrawListSharedDatas)
if (ImGuiContext* imgui_ctx = shared_data->Context)
{
atlas->RendererHasTextures = (imgui_ctx->IO.BackendFlags & ImGuiBackendFlags_RendererHasTextures) != 0;
break;
}
}
void ImFontAtlas::ClearFonts()
// Called by NewFrame() for atlases owned by a context.
// If you manually manage font atlases, you'll need to call this yourself.
// - 'frame_count' needs to be provided because we can gc/prioritize baked fonts based on their age.
// - 'frame_count' may not match those of all imgui contexts using this atlas, as contexts may be updated as different frequencies. But generally you can use ImGui::GetFrameCount() on one of your context.
void ImFontAtlasUpdateNewFrame(ImFontAtlas* atlas, int frame_count, bool renderer_has_textures)
{
IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas between NewFrame() and EndFrame/Render()!");
Fonts.clear_delete();
TexReady = false;
IM_ASSERT(atlas->Builder == NULL || atlas->Builder->FrameCount < frame_count); // Protection against being called twice.
atlas->RendererHasTextures = renderer_has_textures;
// Check that font atlas was built or backend support texture reload in which case we can build now
if (atlas->RendererHasTextures)
{
atlas->TexIsBuilt = true;
if (atlas->Builder == NULL) // This will only happen if fonts were not already loaded.
ImFontAtlasBuildMain(atlas);
}
// Legacy backend
if (!atlas->RendererHasTextures)
IM_ASSERT_USER_ERROR(atlas->TexIsBuilt, "Backend does not support ImGuiBackendFlags_RendererHasTextures, and font atlas is not built! Update backend OR make sure you called ImGui_ImplXXXX_NewFrame() function for renderer backend, which should call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8().");
if (atlas->TexIsBuilt && atlas->Builder->PreloadedAllGlyphsRanges)
IM_ASSERT_USER_ERROR(atlas->RendererHasTextures == false, "Called ImFontAtlas::Build() before ImGuiBackendFlags_RendererHasTextures got set! With new backends: you don't need to call Build().");
// Clear BakedCurrent cache, this is important because it ensure the uncached path gets taken once.
// We also rely on ImFontBaked* pointers never crossing frames.
ImFontAtlasBuilder* builder = atlas->Builder;
builder->FrameCount = frame_count;
for (ImFont* font : atlas->Fonts)
font->LastBaked = NULL;
// Garbage collect BakedPool
if (builder->BakedDiscardedCount > 0)
{
int dst_n = 0, src_n = 0;
for (; src_n < builder->BakedPool.Size; src_n++)
{
ImFontBaked* p_src = &builder->BakedPool[src_n];
if (p_src->WantDestroy)
continue;
ImFontBaked* p_dst = &builder->BakedPool[dst_n++];
if (p_dst == p_src)
continue;
memcpy(p_dst, p_src, sizeof(ImFontBaked));
builder->BakedMap.SetVoidPtr(p_dst->BakedId, p_dst);
}
IM_ASSERT(dst_n + builder->BakedDiscardedCount == src_n);
builder->BakedPool.Size -= builder->BakedDiscardedCount;
builder->BakedDiscardedCount = 0;
}
// Update texture status
for (int tex_n = 0; tex_n < atlas->TexList.Size; tex_n++)
{
ImTextureData* tex = atlas->TexList[tex_n];
bool remove_from_list = false;
if (tex->Status == ImTextureStatus_OK)
{
tex->Updates.resize(0);
tex->UpdateRect.x = tex->UpdateRect.y = (unsigned short)~0;
tex->UpdateRect.w = tex->UpdateRect.h = 0;
}
if (tex->Status == ImTextureStatus_WantCreate && atlas->RendererHasTextures)
IM_ASSERT(tex->TexID == ImTextureID_Invalid && tex->BackendUserData == NULL && "Backend set texture's TexID/BackendUserData but did not update Status to OK.");
// Request destroy
// - Keep bool to true in order to differentiate a planned destroy vs a destroy decided by the backend.
// - We don't destroy pixels right away, as backend may have an in-flight copy from RAM.
if (tex->WantDestroyNextFrame && tex->Status != ImTextureStatus_Destroyed && tex->Status != ImTextureStatus_WantDestroy)
{
IM_ASSERT(tex->Status == ImTextureStatus_OK || tex->Status == ImTextureStatus_WantCreate || tex->Status == ImTextureStatus_WantUpdates);
tex->Status = ImTextureStatus_WantDestroy;
}
// If a texture has never reached the backend, they don't need to know about it.
// (note: backends between 1.92.0 and 1.92.4 could set an already destroyed texture to ImTextureStatus_WantDestroy
// when invalidating graphics objects twice, which would previously remove it from the list and crash.)
if (tex->Status == ImTextureStatus_WantDestroy && tex->TexID == ImTextureID_Invalid && tex->BackendUserData == NULL)
tex->Status = ImTextureStatus_Destroyed;
// Process texture being destroyed
if (tex->Status == ImTextureStatus_Destroyed)
{
IM_ASSERT(tex->TexID == ImTextureID_Invalid && tex->BackendUserData == NULL && "Backend set texture Status to Destroyed but did not clear TexID/BackendUserData!");
if (tex->WantDestroyNextFrame)
remove_from_list = true; // Destroy was scheduled by us
else
tex->Status = ImTextureStatus_WantCreate; // Destroy was done was backend: recreate it (e.g. freed resources mid-run)
}
// The backend may need defer destroying by a few frames, to handle texture used by previous in-flight rendering.
// We allow the texture staying in _WantDestroy state and increment a counter which the backend can use to take its decision.
if (tex->Status == ImTextureStatus_WantDestroy)
tex->UnusedFrames++;
// Destroy and remove
if (remove_from_list)
{
IM_ASSERT(atlas->TexData != tex);
tex->DestroyPixels();
IM_DELETE(tex);
atlas->TexList.erase(atlas->TexList.begin() + tex_n);
tex_n--;
}
}
}
void ImFontAtlas::Clear()
void ImFontAtlasTextureBlockConvert(const unsigned char* src_pixels, ImTextureFormat src_fmt, int src_pitch, unsigned char* dst_pixels, ImTextureFormat dst_fmt, int dst_pitch, int w, int h)
{
ClearInputData();
ClearTexData();
ClearFonts();
IM_ASSERT(src_pixels != NULL && dst_pixels != NULL);
if (src_fmt == dst_fmt)
{
int line_sz = w * ImTextureDataGetFormatBytesPerPixel(src_fmt);
for (int ny = h; ny > 0; ny--, src_pixels += src_pitch, dst_pixels += dst_pitch)
memcpy(dst_pixels, src_pixels, line_sz);
}
else if (src_fmt == ImTextureFormat_Alpha8 && dst_fmt == ImTextureFormat_RGBA32)
{
for (int ny = h; ny > 0; ny--, src_pixels += src_pitch, dst_pixels += dst_pitch)
{
const ImU8* src_p = (const ImU8*)src_pixels;
ImU32* dst_p = (ImU32*)(void*)dst_pixels;
for (int nx = w; nx > 0; nx--)
*dst_p++ = IM_COL32(255, 255, 255, (unsigned int)(*src_p++));
}
}
else if (src_fmt == ImTextureFormat_RGBA32 && dst_fmt == ImTextureFormat_Alpha8)
{
for (int ny = h; ny > 0; ny--, src_pixels += src_pitch, dst_pixels += dst_pitch)
{
const ImU32* src_p = (const ImU32*)(void*)src_pixels;
ImU8* dst_p = (ImU8*)dst_pixels;
for (int nx = w; nx > 0; nx--)
*dst_p++ = ((*src_p++) >> IM_COL32_A_SHIFT) & 0xFF;
}
}
else
{
IM_ASSERT(0);
}
}
void ImFontAtlas::GetTexDataAsAlpha8(unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel)
// Source buffer may be written to (used for in-place mods).
// Post-process hooks may eventually be added here.
void ImFontAtlasTextureBlockPostProcess(ImFontAtlasPostProcessData* data)
{
// Build atlas on demand
if (TexPixelsAlpha8 == NULL)
Build();
// Multiply operator (legacy)
if (data->FontSrc->RasterizerMultiply != 1.0f)
ImFontAtlasTextureBlockPostProcessMultiply(data, data->FontSrc->RasterizerMultiply);
}
*out_pixels = TexPixelsAlpha8;
if (out_width) *out_width = TexWidth;
if (out_height) *out_height = TexHeight;
if (out_bytes_per_pixel) *out_bytes_per_pixel = 1;
void ImFontAtlasTextureBlockPostProcessMultiply(ImFontAtlasPostProcessData* data, float multiply_factor)
{
unsigned char* pixels = (unsigned char*)data->Pixels;
int pitch = data->Pitch;
if (data->Format == ImTextureFormat_Alpha8)
{
for (int ny = data->Height; ny > 0; ny--, pixels += pitch)
{
ImU8* p = (ImU8*)pixels;
for (int nx = data->Width; nx > 0; nx--, p++)
{
unsigned int v = ImMin((unsigned int)(*p * multiply_factor), (unsigned int)255);
*p = (unsigned char)v;
}
}
}
else if (data->Format == ImTextureFormat_RGBA32) //-V547
{
for (int ny = data->Height; ny > 0; ny--, pixels += pitch)
{
ImU32* p = (ImU32*)(void*)pixels;
for (int nx = data->Width; nx > 0; nx--, p++)
{
unsigned int a = ImMin((unsigned int)(((*p >> IM_COL32_A_SHIFT) & 0xFF) * multiply_factor), (unsigned int)255);
*p = IM_COL32((*p >> IM_COL32_R_SHIFT) & 0xFF, (*p >> IM_COL32_G_SHIFT) & 0xFF, (*p >> IM_COL32_B_SHIFT) & 0xFF, a);
}
}
}
else
{
IM_ASSERT(0);
}
}
void ImFontAtlas::GetTexDataAsRGBA32(unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel)
// Fill with single color. We don't use this directly but it is convenient for anyone working on uploading custom rects.
void ImFontAtlasTextureBlockFill(ImTextureData* dst_tex, int dst_x, int dst_y, int w, int h, ImU32 col)
{
// Convert to RGBA32 format on demand
// Although it is likely to be the most commonly used format, our font rendering is 1 channel / 8 bpp
if (!TexPixelsRGBA32)
if (dst_tex->Format == ImTextureFormat_Alpha8)
{
ImU8 col_a = (col >> IM_COL32_A_SHIFT) & 0xFF;
for (int y = 0; y < h; y++)
memset((ImU8*)dst_tex->GetPixelsAt(dst_x, dst_y + y), col_a, w);
}
else
{
unsigned char* pixels = NULL;
GetTexDataAsAlpha8(&pixels, NULL, NULL);
if (pixels)
for (int y = 0; y < h; y++)
{
TexPixelsRGBA32 = (unsigned int*)IM_ALLOC((size_t)TexWidth * (size_t)TexHeight * 4);
const unsigned char* src = pixels;
unsigned int* dst = TexPixelsRGBA32;
for (int n = TexWidth * TexHeight; n > 0; n--)
*dst++ = IM_COL32(255, 255, 255, (unsigned int)(*src++));
ImU32* p = (ImU32*)(void*)dst_tex->GetPixelsAt(dst_x, dst_y + y);
for (int x = w; x > 0; x--, p++)
*p = col;
}
}
}
// Copy block from one texture to another
void ImFontAtlasTextureBlockCopy(ImTextureData* src_tex, int src_x, int src_y, ImTextureData* dst_tex, int dst_x, int dst_y, int w, int h)
{
IM_ASSERT(src_tex->Pixels != NULL && dst_tex->Pixels != NULL);
IM_ASSERT(src_tex->Format == dst_tex->Format);
IM_ASSERT(src_x >= 0 && src_x + w <= src_tex->Width);
IM_ASSERT(src_y >= 0 && src_y + h <= src_tex->Height);
IM_ASSERT(dst_x >= 0 && dst_x + w <= dst_tex->Width);
IM_ASSERT(dst_y >= 0 && dst_y + h <= dst_tex->Height);
for (int y = 0; y < h; y++)
memcpy(dst_tex->GetPixelsAt(dst_x, dst_y + y), src_tex->GetPixelsAt(src_x, src_y + y), w * dst_tex->BytesPerPixel);
}
// Queue texture block update for renderer backend
void ImFontAtlasTextureBlockQueueUpload(ImFontAtlas* atlas, ImTextureData* tex, int x, int y, int w, int h)
{
IM_ASSERT(tex->Status != ImTextureStatus_WantDestroy && tex->Status != ImTextureStatus_Destroyed);
IM_ASSERT(x >= 0 && x <= 0xFFFF && y >= 0 && y <= 0xFFFF && w >= 0 && x + w <= 0x10000 && h >= 0 && y + h <= 0x10000);
IM_UNUSED(atlas);
ImTextureRect req = { (unsigned short)x, (unsigned short)y, (unsigned short)w, (unsigned short)h };
int new_x1 = ImMax(tex->UpdateRect.w == 0 ? 0 : tex->UpdateRect.x + tex->UpdateRect.w, req.x + req.w);
int new_y1 = ImMax(tex->UpdateRect.h == 0 ? 0 : tex->UpdateRect.y + tex->UpdateRect.h, req.y + req.h);
tex->UpdateRect.x = ImMin(tex->UpdateRect.x, req.x);
tex->UpdateRect.y = ImMin(tex->UpdateRect.y, req.y);
tex->UpdateRect.w = (unsigned short)(new_x1 - tex->UpdateRect.x);
tex->UpdateRect.h = (unsigned short)(new_y1 - tex->UpdateRect.y);
tex->UsedRect.x = ImMin(tex->UsedRect.x, req.x);
tex->UsedRect.y = ImMin(tex->UsedRect.y, req.y);
tex->UsedRect.w = (unsigned short)(ImMax(tex->UsedRect.x + tex->UsedRect.w, req.x + req.w) - tex->UsedRect.x);
tex->UsedRect.h = (unsigned short)(ImMax(tex->UsedRect.y + tex->UsedRect.h, req.y + req.h) - tex->UsedRect.y);
atlas->TexIsBuilt = false;
// No need to queue if status is == ImTextureStatus_WantCreate
if (tex->Status == ImTextureStatus_OK || tex->Status == ImTextureStatus_WantUpdates)
{
tex->Status = ImTextureStatus_WantUpdates;
tex->Updates.push_back(req);
}
}
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
static void GetTexDataAsFormat(ImFontAtlas* atlas, ImTextureFormat format, unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel)
{
ImTextureData* tex = atlas->TexData;
if (!atlas->TexIsBuilt || tex == NULL || tex->Pixels == NULL || atlas->TexDesiredFormat != format)
{
atlas->TexDesiredFormat = format;
atlas->Build();
tex = atlas->TexData;
}
if (out_pixels) { *out_pixels = (unsigned char*)tex->Pixels; };
if (out_width) { *out_width = tex->Width; };
if (out_height) { *out_height = tex->Height; };
if (out_bytes_per_pixel) { *out_bytes_per_pixel = tex->BytesPerPixel; }
}
void ImFontAtlas::GetTexDataAsAlpha8(unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel)
{
GetTexDataAsFormat(this, ImTextureFormat_Alpha8, out_pixels, out_width, out_height, out_bytes_per_pixel);
}
void ImFontAtlas::GetTexDataAsRGBA32(unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel)
{
GetTexDataAsFormat(this, ImTextureFormat_RGBA32, out_pixels, out_width, out_height, out_bytes_per_pixel);
}
*out_pixels = (unsigned char*)TexPixelsRGBA32;
if (out_width) *out_width = TexWidth;
if (out_height) *out_height = TexHeight;
if (out_bytes_per_pixel) *out_bytes_per_pixel = 4;
bool ImFontAtlas::Build()
{
ImFontAtlasBuildMain(this);
return true;
}
#endif // #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
ImFont* ImFontAtlas::AddFont(const ImFontConfig* font_cfg)
ImFont* ImFontAtlas::AddFont(const ImFontConfig* font_cfg_in)
{
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 && "Is ImFontConfig struct correctly initialized?");
IM_ASSERT(font_cfg->OversampleH > 0 && font_cfg->OversampleV > 0 && "Is ImFontConfig struct correctly initialized?");
// Sanity Checks
IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas!");
IM_ASSERT((font_cfg_in->FontData != NULL && font_cfg_in->FontDataSize > 0) || (font_cfg_in->FontLoader != NULL));
//IM_ASSERT(font_cfg_in->SizePixels > 0.0f && "Is ImFontConfig struct correctly initialized?");
IM_ASSERT(font_cfg_in->RasterizerDensity > 0.0f && "Is ImFontConfig struct correctly initialized?");
if (font_cfg_in->GlyphOffset.x != 0.0f || font_cfg_in->GlyphOffset.y != 0.0f || font_cfg_in->GlyphMinAdvanceX != 0.0f || font_cfg_in->GlyphMaxAdvanceX != FLT_MAX)
IM_ASSERT(font_cfg_in->SizePixels != 0.0f && "Specifying glyph offset/advances requires a reference size to base it on.");
// Lazily create builder on the first call to AddFont
if (Builder == NULL)
ImFontAtlasBuildInit(this);
// Create new font
if (!font_cfg->MergeMode)
Fonts.push_back(IM_NEW(ImFont));
ImFont* font;
if (!font_cfg_in->MergeMode)
{
font = IM_NEW(ImFont)();
font->FontId = FontNextUniqueID++;
font->Flags = font_cfg_in->Flags;
font->LegacySize = font_cfg_in->SizePixels;
font->CurrentRasterizerDensity = font_cfg_in->RasterizerDensity;
Fonts.push_back(font);
}
else
{
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.
font = font_cfg_in->DstFont ? font_cfg_in->DstFont : Fonts.back();
}
// Add to list
Sources.push_back(*font_cfg_in);
ImFontConfig* font_cfg = &Sources.back();
if (font_cfg->DstFont == NULL)
font_cfg->DstFont = font;
font->Sources.push_back(font_cfg);
ImFontAtlasBuildUpdatePointers(this); // Pointers to Sources are otherwise dangling after we called Sources.push_back().
ConfigData.push_back(*font_cfg);
ImFontConfig& new_font_cfg = ConfigData.back();
if (new_font_cfg.DstFont == NULL)
new_font_cfg.DstFont = Fonts.back();
if (!new_font_cfg.FontDataOwnedByAtlas)
if (font_cfg->FontDataOwnedByAtlas == false)
{
new_font_cfg.FontData = IM_ALLOC(new_font_cfg.FontDataSize);
new_font_cfg.FontDataOwnedByAtlas = true;
memcpy(new_font_cfg.FontData, font_cfg->FontData, (size_t)new_font_cfg.FontDataSize);
font_cfg->FontDataOwnedByAtlas = true;
font_cfg->FontData = ImMemdup(font_cfg->FontData, (size_t)font_cfg->FontDataSize);
}
if (new_font_cfg.DstFont->EllipsisChar == (ImWchar)-1)
new_font_cfg.DstFont->EllipsisChar = font_cfg->EllipsisChar;
// Sanity check
// We don't round cfg.SizePixels yet as relative size of merged fonts are used afterwards.
if (font_cfg->GlyphExcludeRanges != NULL)
{
int size = 0;
for (const ImWchar* p = font_cfg->GlyphExcludeRanges; p[0] != 0; p++, size++) {}
IM_ASSERT((size & 1) == 0 && "GlyphExcludeRanges[] size must be multiple of two!");
IM_ASSERT((size <= 64) && "GlyphExcludeRanges[] size must be small!");
font_cfg->GlyphExcludeRanges = (ImWchar*)ImMemdup(font_cfg->GlyphExcludeRanges, sizeof(font_cfg->GlyphExcludeRanges[0]) * (size + 1));
}
if (font_cfg->FontLoader != NULL)
{
IM_ASSERT(font_cfg->FontLoader->FontBakedLoadGlyph != NULL);
IM_ASSERT(font_cfg->FontLoader->LoaderInit == NULL && font_cfg->FontLoader->LoaderShutdown == NULL); // FIXME-NEWATLAS: Unsupported yet.
}
IM_ASSERT(font_cfg->FontLoaderData == NULL);
ImFontAtlasUpdateConfigDataPointers(this);
if (!ImFontAtlasFontSourceInit(this, font_cfg))
{
// Rollback (this is a fragile/rarely exercised code-path. TestSuite's "misc_atlas_add_invalid_font" aim to test this)
ImFontAtlasFontDestroySourceData(this, font_cfg);
Sources.pop_back();
font->Sources.pop_back();
if (!font_cfg->MergeMode)
{
IM_DELETE(font);
Fonts.pop_back();
}
return NULL;
}
ImFontAtlasFontSourceAddToFont(this, font, font_cfg);
// Invalidate texture
TexReady = false;
ClearTexData();
return new_font_cfg.DstFont;
return font;
}
// Default font TTF is compressed with stb_compress then base85 encoded (see misc/fonts/binary_to_compressed_c.cpp for encoder)
static unsigned int stb_decompress_length(const unsigned char* input);
static unsigned int stb_decompress(unsigned char* output, const unsigned char* input, unsigned int length);
static const char* GetDefaultCompressedFontDataTTFBase85();
static unsigned int Decode85Byte(char c) { return c >= '\\' ? c-36 : c-35; }
static void Decode85(const unsigned char* src, unsigned char* dst)
{
......
dst += 4;
}
}
#ifndef IMGUI_DISABLE_DEFAULT_FONT
static const char* GetDefaultCompressedFontDataTTF(int* out_size);
#endif
// Load embedded ProggyClean.ttf at size 13, disable oversampling
// If you want a similar font which may be better scaled, consider using ProggyVector from the same author!
ImFont* ImFontAtlas::AddFontDefault(const ImFontConfig* font_cfg_template)
{
#ifndef IMGUI_DISABLE_DEFAULT_FONT
ImFontConfig font_cfg = font_cfg_template ? *font_cfg_template : ImFontConfig();
if (!font_cfg_template)
{
......
if (font_cfg.SizePixels <= 0.0f)
font_cfg.SizePixels = 13.0f * 1.0f;
if (font_cfg.Name[0] == '\0')
ImFormatString(font_cfg.Name, IM_ARRAYSIZE(font_cfg.Name), "ProggyClean.ttf, %dpx", (int)font_cfg.SizePixels);
ImFormatString(font_cfg.Name, IM_ARRAYSIZE(font_cfg.Name), "ProggyClean.ttf");
font_cfg.EllipsisChar = (ImWchar)0x0085;
font_cfg.GlyphOffset.y = 1.0f * IM_TRUNC(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();
ImFont* font = AddFontFromMemoryCompressedBase85TTF(ttf_compressed_base85, font_cfg.SizePixels, &font_cfg, glyph_ranges);
return font;
int ttf_compressed_size = 0;
const char* ttf_compressed = GetDefaultCompressedFontDataTTF(&ttf_compressed_size);
return AddFontFromMemoryCompressedTTF(ttf_compressed, ttf_compressed_size, font_cfg.SizePixels, &font_cfg);
#else
IM_ASSERT(0 && "AddFontDefault() disabled in this build.");
IM_UNUSED(font_cfg_template);
return NULL;
#endif // #ifndef IMGUI_DISABLE_DEFAULT_FONT
}
ImFont* ImFontAtlas::AddFontFromFileTTF(const char* filename, 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()!");
IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas!");
size_t data_size = 0;
void* data = ImFileLoadToMemory(filename, "rb", &data_size, 0);
if (!data)
{
IM_ASSERT_USER_ERROR(0, "Could not load font file!");
if (font_cfg_template == NULL || (font_cfg_template->Flags & ImFontFlags_NoLoadError) == 0)
{
IMGUI_DEBUG_LOG("While loading '%s'\n", filename);
IM_ASSERT_USER_ERROR(0, "Could not load font file!");
}
return NULL;
}
ImFontConfig font_cfg = font_cfg_template ? *font_cfg_template : ImFontConfig();
......
{
// Store a short copy of filename into into the font name for convenience
const char* p;
for (p = filename + strlen(filename); p > filename && p[-1] != '/' && p[-1] != '\\'; p--) {}
ImFormatString(font_cfg.Name, IM_ARRAYSIZE(font_cfg.Name), "%s, %.0fpx", p, size_pixels);
for (p = filename + ImStrlen(filename); p > filename && p[-1] != '/' && p[-1] != '\\'; p--) {}
ImFormatString(font_cfg.Name, IM_ARRAYSIZE(font_cfg.Name), "%s", p);
}
return AddFontFromMemoryTTF(data, (int)data_size, size_pixels, &font_cfg, glyph_ranges);
}
......
// 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* 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()!");
IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas!");
ImFontConfig font_cfg = font_cfg_template ? *font_cfg_template : ImFontConfig();
IM_ASSERT(font_cfg.FontData == NULL);
IM_ASSERT(font_data_size > 100 && "Incorrect value for font_data_size!"); // Heuristic to prevent accidentally passing a wrong value to font_data_size.
......
ImFont* ImFontAtlas::AddFontFromMemoryCompressedBase85TTF(const char* compressed_ttf_data_base85, float size_pixels, const ImFontConfig* font_cfg, const ImWchar* glyph_ranges)
{
int compressed_ttf_size = (((int)strlen(compressed_ttf_data_base85) + 4) / 5) * 4;
int compressed_ttf_size = (((int)ImStrlen(compressed_ttf_data_base85) + 4) / 5) * 4;
void* compressed_ttf = IM_ALLOC((size_t)compressed_ttf_size);
Decode85((const unsigned char*)compressed_ttf_data_base85, (unsigned char*)compressed_ttf);
ImFont* font = AddFontFromMemoryCompressedTTF(compressed_ttf, compressed_ttf_size, size_pixels, font_cfg, glyph_ranges);
......
return font;
}
int ImFontAtlas::AddCustomRectRegular(int width, int height)
// On font removal we need to remove references (otherwise we could queue removal?)
// We allow old_font == new_font which forces updating all values (e.g. sizes)
void ImFontAtlasBuildNotifySetFont(ImFontAtlas* atlas, ImFont* old_font, ImFont* new_font)
{
for (ImDrawListSharedData* shared_data : atlas->DrawListSharedDatas)
{
if (shared_data->Font == old_font)
shared_data->Font = new_font;
if (ImGuiContext* ctx = shared_data->Context)
{
if (ctx->IO.FontDefault == old_font)
ctx->IO.FontDefault = new_font;
if (ctx->Font == old_font)
{
ImGuiContext* curr_ctx = ImGui::GetCurrentContext();
bool need_bind_ctx = ctx != curr_ctx;
if (need_bind_ctx)
ImGui::SetCurrentContext(ctx);
ImGui::SetCurrentFont(new_font, ctx->FontSizeBase, ctx->FontSize);
if (need_bind_ctx)
ImGui::SetCurrentContext(curr_ctx);
}
for (ImFontStackData& font_stack_data : ctx->FontStack)
if (font_stack_data.Font == old_font)
font_stack_data.Font = new_font;
}
}
}
void ImFontAtlas::RemoveFont(ImFont* font)
{
IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas!");
font->ClearOutputData();
ImFontAtlasFontDestroyOutput(this, font);
for (ImFontConfig* src : font->Sources)
ImFontAtlasFontDestroySourceData(this, src);
for (int src_n = 0; src_n < Sources.Size; src_n++)
if (Sources[src_n].DstFont == font)
Sources.erase(&Sources[src_n--]);
bool removed = Fonts.find_erase(font);
IM_ASSERT(removed);
IM_UNUSED(removed);
ImFontAtlasBuildUpdatePointers(this);
font->OwnerAtlas = NULL;
IM_DELETE(font);
// Notify external systems
ImFont* new_current_font = Fonts.empty() ? NULL : Fonts[0];
ImFontAtlasBuildNotifySetFont(this, font, new_current_font);
}
// At it is common to do an AddCustomRect() followed by a GetCustomRect(), we provide an optional 'ImFontAtlasRect* out_r = NULL' argument to retrieve the info straight away.
ImFontAtlasRectId ImFontAtlas::AddCustomRect(int width, int height, ImFontAtlasRect* out_r)
{
IM_ASSERT(width > 0 && width <= 0xFFFF);
IM_ASSERT(height > 0 && height <= 0xFFFF);
ImFontAtlasCustomRect r;
r.Width = (unsigned short)width;
r.Height = (unsigned short)height;
CustomRects.push_back(r);
return CustomRects.Size - 1; // Return index
if (Builder == NULL)
ImFontAtlasBuildInit(this);
ImFontAtlasRectId r_id = ImFontAtlasPackAddRect(this, width, height);
if (r_id == ImFontAtlasRectId_Invalid)
return ImFontAtlasRectId_Invalid;
if (out_r != NULL)
GetCustomRect(r_id, out_r);
if (RendererHasTextures)
{
ImTextureRect* r = ImFontAtlasPackGetRect(this, r_id);
ImFontAtlasTextureBlockQueueUpload(this, TexData, r->x, r->y, r->w, r->h);
}
return r_id;
}
int ImFontAtlas::AddCustomRectFontGlyph(ImFont* font, ImWchar id, int width, int height, float advance_x, const ImVec2& offset)
void ImFontAtlas::RemoveCustomRect(ImFontAtlasRectId id)
{
if (ImFontAtlasPackGetRectSafe(this, id) == NULL)
return;
ImFontAtlasPackDiscardRect(this, id);
}
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
// This API does not make sense anymore with scalable fonts.
// - Prefer adding a font source (ImFontConfig) using a custom/procedural loader.
// - You may use ImFontFlags_LockBakedSizes to limit an existing font to known baked sizes:
// ImFont* myfont = io.Fonts->AddFontFromFileTTF(....);
// myfont->GetFontBaked(16.0f);
// myfont->Flags |= ImFontFlags_LockBakedSizes;
ImFontAtlasRectId ImFontAtlas::AddCustomRectFontGlyph(ImFont* font, ImWchar codepoint, int width, int height, float advance_x, const ImVec2& offset)
{
float font_size = font->LegacySize;
return AddCustomRectFontGlyphForSize(font, font_size, codepoint, width, height, advance_x, offset);
}
// FIXME: we automatically set glyph.Colored=true by default.
// If you need to alter this, you can write 'font->Glyphs.back()->Colored' after calling AddCustomRectFontGlyph().
ImFontAtlasRectId ImFontAtlas::AddCustomRectFontGlyphForSize(ImFont* font, float font_size, ImWchar codepoint, int width, int height, float advance_x, const ImVec2& offset)
{
#ifdef IMGUI_USE_WCHAR32
IM_ASSERT(id <= IM_UNICODE_CODEPOINT_MAX);
IM_ASSERT(codepoint <= IM_UNICODE_CODEPOINT_MAX);
#endif
IM_ASSERT(font != NULL);
IM_ASSERT(width > 0 && width <= 0xFFFF);
IM_ASSERT(height > 0 && height <= 0xFFFF);
ImFontAtlasCustomRect r;
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;
CustomRects.push_back(r);
return CustomRects.Size - 1; // Return index
}
void ImFontAtlas::CalcCustomRectUV(const ImFontAtlasCustomRect* rect, ImVec2* out_uv_min, ImVec2* out_uv_max) const
{
IM_ASSERT(TexWidth > 0 && TexHeight > 0); // Font atlas needs to be built before we can calculate UV coordinates
IM_ASSERT(rect->IsPacked()); // Make sure the rectangle has been packed
*out_uv_min = ImVec2((float)rect->X * TexUvScale.x, (float)rect->Y * TexUvScale.y);
*out_uv_max = ImVec2((float)(rect->X + rect->Width) * TexUvScale.x, (float)(rect->Y + rect->Height) * TexUvScale.y);
ImFontBaked* baked = font->GetFontBaked(font_size);
ImFontAtlasRectId r_id = ImFontAtlasPackAddRect(this, width, height);
if (r_id == ImFontAtlasRectId_Invalid)
return ImFontAtlasRectId_Invalid;
ImTextureRect* r = ImFontAtlasPackGetRect(this, r_id);
if (RendererHasTextures)
ImFontAtlasTextureBlockQueueUpload(this, TexData, r->x, r->y, r->w, r->h);
... This diff was truncated because it exceeds the maximum size that can be displayed.

Also available in: Unified diff