Revision a8009d0e
Added by david.sorber 8 months ago
| 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);
|
||
|
|
||
Update Dear ImGui to version 1.92.5.