1
0
Fork 0
mirror of https://github.com/ocornut/imgui.git synced 2026-01-11 00:04:24 +00:00

Fonts: Baked system, with auto-bind, v10.

# Conflicts:
#	imgui_internal.h
This commit is contained in:
ocornut 2025-01-23 15:46:22 +01:00
parent 7aba8da551
commit 093d01269a
7 changed files with 545 additions and 296 deletions

View file

@ -2394,6 +2394,7 @@ void ImGui::ShadeVertsTransformPos(ImDrawList* draw_list, int vert_start_idx, in
// [SECTION] ImFontConfig
//-----------------------------------------------------------------------------
// FIXME-NEWATLAS: Oversample specification could be more dynamic. For now, favoring automatic selection.
ImFontConfig::ImFontConfig()
{
memset(this, 0, sizeof(*this));
@ -2495,9 +2496,9 @@ void ImTextureData::DestroyPixels()
// - ImFontAtlasBuildAddFont()
// - ImFontAtlasBuildSetupFontCreateEllipsisFromDot()
// - ImFontAtlasBuildSetupFontSpecialGlyphs()
// - ImFontAtlasBuildDiscardFontGlyph()
// - ImFontAtlasBuildDiscardFontGlyphs()
// - ImFontAtlasBuildReloadFont()
// - ImFontAtlasBuildDiscardUnusedBakes()
// - ImFontAtlasBuildDiscardFontBaked()
// - ImFontAtlasBuildDiscardFontBakedGlyph()
//-----------------------------------------------------------------------------
// - ImFontAtlasAddDrawListSharedData()
// - ImFontAtlasRemoveDrawListSharedData()
@ -2594,6 +2595,7 @@ ImFontAtlas::ImFontAtlas()
RendererHasTextures = false; // Assumed false by default, as apps can call e.g Atlas::Build() after backend init and before ImGui can update.
TexRef._TexData = NULL;// this;
TexNextUniqueID = 1;
FontNextUniqueID = 1;
Builder = NULL;
}
@ -2685,7 +2687,8 @@ static void ImFontAtlasBuildUpdateRendererHasTexturesFromContext(ImFontAtlas* at
// Called by NewFrame(). When multiple context own the atlas, only the first one calls this.
// If you are calling this yourself, ensure atlas->RendererHasTexUpdates is et.
void ImFontAtlasUpdateNewFrame(ImFontAtlas* atlas)
// 'frame_count' needs to be provided because we can gc/prioritize baked fonts based on their age.
void ImFontAtlasUpdateNewFrame(ImFontAtlas* atlas, int frame_count)
{
// Check that font atlas was built or backend support texture reload in which case we can build now
if (atlas->RendererHasTextures)
@ -2701,6 +2704,33 @@ void ImFontAtlasUpdateNewFrame(ImFontAtlas* atlas)
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++)
{
@ -2928,15 +2958,23 @@ ImFont* ImFontAtlas::AddFont(const ImFontConfig* font_cfg)
ImFontAtlasBuildInit(this);
// Create new font
ImFont* font;
if (!font_cfg->MergeMode)
Fonts.push_back(IM_NEW(ImFont));
{
font = IM_NEW(ImFont)();
font->FontId = FontNextUniqueID++;
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 = Fonts.back();
}
Sources.push_back(*font_cfg);
ImFontConfig& new_font_cfg = Sources.back();
if (new_font_cfg.DstFont == NULL)
new_font_cfg.DstFont = Fonts.back();
new_font_cfg.DstFont = font;
if (!new_font_cfg.FontDataOwnedByAtlas)
{
new_font_cfg.FontData = IM_ALLOC(new_font_cfg.FontDataSize);
@ -2960,7 +2998,7 @@ ImFont* ImFontAtlas::AddFont(const ImFontConfig* font_cfg)
Sources.pop_back();
if (!font_cfg->MergeMode)
{
IM_DELETE(Fonts.back());
IM_DELETE(font);
Fonts.pop_back();
}
return NULL;
@ -3091,7 +3129,7 @@ static void ImFontAtlasBuildNotifySetFont(ImFontAtlas* atlas, ImFont* old_font,
bool need_bind_ctx = ctx != curr_ctx;
if (need_bind_ctx)
ImGui::SetCurrentContext(ctx);
ImGui::SetCurrentFont(new_font);
ImGui::SetCurrentFont(new_font, ctx->FontSize);
if (need_bind_ctx)
ImGui::SetCurrentContext(curr_ctx);
}
@ -3102,7 +3140,7 @@ static void ImFontAtlasBuildNotifySetFont(ImFontAtlas* atlas, ImFont* old_font,
void ImFontAtlas::RemoveFont(ImFont* font)
{
IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas!");
ImFontAtlasBuildDiscardFontGlyphs(this, font);
font->ClearOutputData();
for (int src_n = 0; src_n < font->SourcesCount; src_n++)
{
@ -3151,7 +3189,7 @@ int ImFontAtlas::AddCustomRectFontGlyph(ImFont* font, ImWchar codepoint, int wid
IM_ASSERT(font != NULL);
IM_ASSERT(width > 0 && width <= 0xFFFF);
IM_ASSERT(height > 0 && height <= 0xFFFF);
#if 0
ImFontAtlasRectId r_id = ImFontAtlasPackAddRect(this, width, height);
if (r_id < 0)
return -1;
@ -3174,6 +3212,12 @@ int ImFontAtlas::AddCustomRectFontGlyph(ImFont* font, ImWchar codepoint, int wid
glyph.PackId = r_id;
ImFontAtlasBuildAddFontGlyph(this, font, &font->Sources[0], &glyph);
return r_id;
#endif
// FIXME-BAKED: Need a design for AddCustomRectFontGlyph()
IM_UNUSED(codepoint);
IM_UNUSED(offset);
IM_UNUSED(advance_x);
return -1;
}
ImTextureRect* ImFontAtlas::GetCustomRectByIndex(int idx)
@ -3237,10 +3281,10 @@ void ImFontAtlasBuildMain(ImFontAtlas* atlas)
atlas->TexIsBuilt = true;
}
void ImFontAtlasBuildGetOversampleFactors(ImFontConfig* src, int* out_oversample_h, int* out_oversample_v)
void ImFontAtlasBuildGetOversampleFactors(ImFontConfig* src, float size, int* out_oversample_h, int* out_oversample_v)
{
// Automatically disable horizontal oversampling over size 36
*out_oversample_h = (src->OversampleH != 0) ? src->OversampleH : (src->SizePixels * src->RasterizerDensity > 36.0f || src->PixelSnapH) ? 1 : 2;
*out_oversample_h = (src->OversampleH != 0) ? src->OversampleH : ((size * src->RasterizerDensity > 36.0f) || src->PixelSnapH) ? 1 : 2;
*out_oversample_v = (src->OversampleV != 0) ? src->OversampleV : 1;
}
@ -3277,11 +3321,12 @@ void ImFontAtlasBuildPreloadAllGlyphRanges(ImFontAtlas* atlas)
for (int src_n = 0; src_n < font->SourcesCount; src_n++)
{
ImFontConfig* src = &font->Sources[src_n];
ImFontBaked* baked = font->GetFontBaked(src->SizePixels);
const ImWchar* ranges = src->GlyphRanges ? src->GlyphRanges : atlas->GetGlyphRangesDefault();
IM_ASSERT(ranges != NULL);
for (; ranges[0]; ranges += 2)
for (unsigned int c = ranges[0]; c <= ranges[1] && c <= IM_UNICODE_CODEPOINT_MAX; c++) //-V560
font->FindGlyphNoFallback((ImWchar)c);
baked->FindGlyphNoFallback((ImWchar)c);
}
}
@ -3436,22 +3481,23 @@ static void ImFontAtlasBuildUpdateLinesTexData(ImFontAtlas* atlas, bool add_and_
//-----------------------------------------------------------------------------------------------------------------------------
static const ImFontGlyph* LoadFirstExistingGlyph(ImFont* font, const ImWchar* candidate_chars, int candidate_chars_count)
ImGuiID ImFontAtlasBakedGetId(ImGuiID font_id, float baked_size)
{
for (int n = 0; n < candidate_chars_count; n++)
if (candidate_chars[n] != 0)
if (const ImFontGlyph* glyph = font->FindGlyphNoFallback(candidate_chars[n]))
return glyph;
return NULL;
struct { ImGuiID FontId; float BakedSize; } hashed_data;
hashed_data.FontId = font_id;
hashed_data.BakedSize = baked_size;
return ImHashData(&hashed_data, sizeof(hashed_data));
}
//-----------------------------------------------------------------------------------------------------------------------------
bool ImFontAtlasBuildAddFont(ImFontAtlas* atlas, ImFontConfig* src)
{
ImFont* font = src->DstFont;
if (src->MergeMode == false)
{
font->ClearOutputData();
font->FontSize = src->SizePixels;
//font->FontSize = src->SizePixels;
font->ContainerAtlas = atlas;
IM_ASSERT(font->Sources == src);
}
@ -3466,15 +3512,15 @@ bool ImFontAtlasBuildAddFont(ImFontAtlas* atlas, ImFontConfig* src)
// Rasterize our own ellipsis character from a dot.
// This may seem overly complicated right now but the point is to exercise and improve a technique which should be increasingly used.
// FIXME-NEWATLAS: This borrows too much from FontLoader's FontAddGlyph() and suggest that we should add further helpers.
static void ImFontAtlasBuildSetupFontCreateEllipsisFromDot(ImFontAtlas* atlas, ImFont* font, const ImFontGlyph* dot_glyph)
// FIXME-NEWATLAS: This borrows too much from FontBackend_FontAddGlyph() and suggest that we should add further helpers.
// FIXME-BAKED: prebaked ellipsis
/*static void ImFontAtlasBuildSetupFontCreateEllipsisFromDot(ImFontAtlas* atlas, ImFont* font, const ImFontGlyph* dot_glyph)
{
ImFontAtlasRect* dot_r = ImFontAtlasPackGetRect(atlas, dot_glyph->PackId);
const int dot_spacing = 1;
const float dot_step = (dot_glyph->X1 - dot_glyph->X0) + dot_spacing;
ImFontAtlasRectId pack_id = ImFontAtlasPackAddRect(atlas, (dot_r->w * 3 + dot_spacing * 2), dot_r->h);
ImFontAtlasRect* r = ImFontAtlasPackGetRect(atlas, pack_id);
font->MetricsTotalSurface += r->w * r->h;
ImFontGlyph glyph;
glyph.Codepoint = (ImWchar)0x0085; // FIXME: Using arbitrary codepoint.
@ -3494,6 +3540,33 @@ static void ImFontAtlasBuildSetupFontCreateEllipsisFromDot(ImFontAtlas* atlas, I
for (int n = 0; n < 3; n++)
ImFontAtlasTextureBlockCopy(tex, dot_r->x, dot_r->y, tex, r->x + (dot_r->w + dot_spacing) * n, r->y, dot_r->w, dot_r->h);
ImFontAtlasTextureBlockQueueUpload(atlas, tex, r->x, r->y, r->w, r->h);
}*/
static void ImFontAtlasBuildSetupFontBakedSpecialGlyphs(ImFontAtlas* atlas, ImFont* font, ImFontBaked* baked)
{
// FIXME-NEWATLAS: could we use a scheme where this is lazily loaded?
IM_ASSERT(baked->FallbackGlyphIndex == -1);
if (font->FallbackChar != 0)
if (const ImFontGlyph* glyph = baked->FindGlyphNoFallback(font->FallbackChar))
{
baked->FallbackGlyphIndex = baked->Glyphs.index_from_ptr(glyph); // Storing index avoid need to update pointer on growth.
baked->FallbackAdvanceX = glyph->AdvanceX;
}
// Mark space as always hidden (not strictly correct/necessary. but some e.g. icons fonts don't have a space. it tends to look neater in previews)
ImFontGlyph* space_glyph = (ImFontGlyph*)(void*)baked->FindGlyphNoFallback((ImWchar)' ');
if (space_glyph != NULL)
space_glyph->Visible = false;
// Setup Tab character.
// FIXME: Needs proper TAB handling but it needs to be contextualized (or we could arbitrary say that each string starts at "column 0" ?)
if (baked->FindGlyphNoFallback('\t') == NULL && space_glyph != NULL)
{
ImFontGlyph tab_glyph;
tab_glyph.Codepoint = '\t';
tab_glyph.AdvanceX = space_glyph->AdvanceX * IM_TABSIZE;
ImFontAtlasBakedAddFontGlyph(atlas, baked, NULL, &tab_glyph);
}
}
// Load/identify special glyphs
@ -3507,31 +3580,15 @@ void ImFontAtlasBuildSetupFontSpecialGlyphs(ImFontAtlas* atlas, ImFont* font, Im
// While manipulating glyphs during init we want to restrict all searches for one source font.
font->LockSingleSrcConfigIdx = (short)src_idx_in_font;
// Setup Fallback character
// FIXME-NEWATLAS: could we use a scheme where this is lazily loaded?
// Find Fallback character. Actual glyph loaded in GetFontBaked().
const ImWchar fallback_chars[] = { font->FallbackChar, (ImWchar)IM_UNICODE_CODEPOINT_INVALID, (ImWchar)'?', (ImWchar)' ' };
if (font->FallbackGlyphIndex == -1)
if (const ImFontGlyph* glyph = LoadFirstExistingGlyph(font, fallback_chars, IM_ARRAYSIZE(fallback_chars)))
{
font->FallbackChar = (ImWchar)glyph->Codepoint;
font->FallbackGlyphIndex = font->Glyphs.index_from_ptr(glyph); // Storing index avoid need to update pointer on growth.
font->FallbackAdvanceX = glyph->AdvanceX;
}
// Mark space as always hidden (not strictly correct/necessary. but some e.g. icons fonts don't have a space. it tends to look neater in previews)
ImFontGlyph* space_glyph = (ImFontGlyph*)(void*)font->FindGlyph((ImWchar)' ');
if (space_glyph != NULL)
space_glyph->Visible = false;
// Setup Tab character.
// FIXME: Needs proper TAB handling but it needs to be contextualized (or we could arbitrary say that each string starts at "column 0" ?)
if (font->FindGlyphNoFallback('\t') == NULL && space_glyph != NULL)
{
ImFontGlyph tab_glyph;
tab_glyph.Codepoint = '\t';
tab_glyph.AdvanceX = space_glyph->AdvanceX * IM_TABSIZE;
ImFontAtlasBuildAddFontGlyph(atlas, font, src, &tab_glyph);
}
if (font->FallbackChar == 0)
for (ImWchar candidate_char : fallback_chars)
if (candidate_char != 0 && font->IsGlyphInFont(candidate_char)) // FIXME: does not respect LockSingleSrcConfigIdx()
{
font->FallbackChar = (ImWchar)candidate_char;
break;
}
// Setup Ellipsis character. It is required for rendering elided text. We prefer using U+2026 (horizontal ellipsis).
// However some old fonts may contain ellipsis at U+0085. Here we auto-detect most suitable ellipsis character.
@ -3546,16 +3603,16 @@ void ImFontAtlasBuildSetupFontSpecialGlyphs(ImFontAtlas* atlas, ImFont* font, Im
}
if (font->EllipsisChar == 0)
{
const ImWchar dots_chars[] = { (ImWchar)'.', (ImWchar)0xFF0E };
/*const ImWchar dots_chars[] = { (ImWchar)'.', (ImWchar)0xFF0E };
if (const ImFontGlyph* dot_glyph = LoadFirstExistingGlyph(font, dots_chars, IM_ARRAYSIZE(dots_chars)))
ImFontAtlasBuildSetupFontCreateEllipsisFromDot(atlas, font, dot_glyph);
else
else*/
font->EllipsisChar = (ImWchar)' ';
}
font->LockSingleSrcConfigIdx = -1;
}
void ImFontAtlasBuildDiscardFontGlyph(ImFontAtlas* atlas, ImFont* font, ImFontGlyph* glyph)
void ImFontAtlasBuildDiscardFontBakedGlyph(ImFontAtlas* atlas, ImFont* font, ImFontBaked* baked, ImFontGlyph* glyph)
{
if (glyph->PackId >= 0)
{
@ -3564,41 +3621,45 @@ void ImFontAtlasBuildDiscardFontGlyph(ImFontAtlas* atlas, ImFont* font, ImFontGl
}
ImWchar c = glyph->Codepoint;
IM_ASSERT(font->FallbackChar != c && font->EllipsisChar != c); // Unsupported for simplicity
IM_ASSERT(glyph >= font->Glyphs.Data && glyph < font->Glyphs.Data + font->Glyphs.Size);
font->IndexLookup[c] = (ImWchar)IM_FONTGLYPH_INDEX_UNUSED;
font->IndexAdvanceX[c] = font->FallbackAdvanceX;
IM_ASSERT(glyph >= baked->Glyphs.Data && glyph < baked->Glyphs.Data + baked->Glyphs.Size);
baked->IndexLookup[c] = IM_FONTGLYPH_INDEX_UNUSED;
baked->IndexAdvanceX[c] = baked->FallbackAdvanceX;
}
void ImFontAtlasBuildDiscardFontGlyphs(ImFontAtlas* atlas, ImFont* font)
void ImFontAtlasBuildDiscardFontBaked(ImFontAtlas* atlas, ImFontBaked* baked)
{
for (ImFontGlyph& glyph : font->Glyphs)
ImGuiContext& g = *GImGui;
IM_UNUSED(g); // for IMGUI_DEBUG_LOG_FONT()
ImFontAtlasBuilder* builder = atlas->Builder;
ImFont* font = baked->ContainerFont;
IMGUI_DEBUG_LOG_FONT("[font] Discard baked %.2f for \"%s\"\n", baked->Size, font->GetDebugName());
for (ImFontGlyph& glyph : baked->Glyphs)
if (glyph.PackId >= 0)
ImFontAtlasPackDiscardRect(atlas, glyph.PackId);
font->ClearOutputData();
font->FallbackChar = font->EllipsisChar = 0;
if (atlas->FontLoader->FontBakedDestroy)
atlas->FontLoader->FontBakedDestroy(atlas, baked);
builder->BakedMap.SetVoidPtr(baked->BakedId, NULL);
builder->BakedDiscardedCount++;
baked->ClearOutputData();
baked->WantDestroy = true;
font->LastBaked = NULL;
}
// Discard old glyphs and reload font. Use if changing font size.
void ImFontAtlasBuildReloadFont(ImFontAtlas* atlas, ImFont* font)
void ImFontAtlasBuildDiscardUnusedBakes(ImFontAtlas* atlas, ImFont* font_filter)
{
ImFontAtlasBuildDiscardFontGlyphs(atlas, font);
for (int src_n = 0; src_n < font->SourcesCount; src_n++)
ImFontAtlasBuilder* builder = atlas->Builder;
const int GC_FRAMES = 2;
for (int baked_n = 0; baked_n < builder->BakedPool.Size; baked_n++)
{
ImFontConfig* src = (ImFontConfig*)(void*)&font->Sources[src_n];
IM_ASSERT(src->SizePixels > 0.0f);
// Reload font in backend
if (atlas->FontLoader && atlas->FontLoader->FontSrcDestroy != NULL)
atlas->FontLoader->FontSrcDestroy(atlas, src);
if (atlas->FontLoader && atlas->FontLoader->FontSrcInit != NULL)
atlas->FontLoader->FontSrcInit(atlas, src);
ImFontAtlasBuildSetupFontSpecialGlyphs(atlas, font, src); // Technically this is called for each source sub-font, tho 99.9% of the time the first one fills everything.
atlas->TexIsBuilt = false;
ImFontBaked* baked = &builder->BakedPool[baked_n];
if (font_filter == NULL || baked->ContainerFont == font_filter)
if (baked->LastUsedFrame + GC_FRAMES < atlas->Builder->FrameCount && !baked->WantDestroy)
ImFontAtlasBuildDiscardFontBaked(atlas, baked);
}
// Notify external systems
ImFontAtlasBuildNotifySetFont(atlas, font, font);
}
// Those functions are designed to facilitate changing the underlying structures for ImFontAtlas to store an array of ImDrawListSharedData*
@ -3747,8 +3808,8 @@ void ImFontAtlasBuildRepackTexture(ImFontAtlas* atlas, int w, int h)
builder->RectsDiscardedSurface = 0;
// Patch glyphs UV
for (ImFont* font : atlas->Fonts)
for (ImFontGlyph& glyph : font->Glyphs)
for (int baked_n = 0; baked_n < builder->BakedPool.Size; baked_n++)
for (ImFontGlyph& glyph : builder->BakedPool[baked_n].Glyphs)
if (glyph.PackId != -1)
{
ImFontAtlasRect* r = ImFontAtlasPackGetRect(atlas, glyph.PackId);
@ -3798,8 +3859,11 @@ void ImFontAtlasBuildGrowTexture(ImFontAtlas* atlas, int old_tex_w, int old_tex_
void ImFontAtlasBuildMakeSpace(ImFontAtlas* atlas)
{
// Currently using a heuristic for repack without growing.
// Can some baked contents be ditched?
ImFontAtlasBuilder* builder = atlas->Builder;
ImFontAtlasBuildDiscardUnusedBakes(atlas, NULL);
// Currently using a heuristic for repack without growing.
if (builder->RectsDiscardedSurface < builder->RectsPackedSurface * 0.20f)
ImFontAtlasBuildGrowTexture(atlas);
else
@ -3845,6 +3909,8 @@ ImVec2i ImFontAtlasBuildGetTextureSizeEstimate(ImFontAtlas* atlas)
void ImFontAtlasBuildCompactTexture(ImFontAtlas* atlas)
{
ImFontAtlasBuilder* builder = atlas->Builder;
ImFontAtlasBuildDiscardUnusedBakes(atlas, NULL);
ImTextureData* old_tex = atlas->TexData;
ImVec2i old_tex_size = ImVec2i(old_tex->Width, old_tex->Height);
ImVec2i new_tex_size = ImFontAtlasBuildGetTextureSizeEstimate(atlas);
@ -3927,7 +3993,7 @@ void ImFontAtlasPackInit(ImFontAtlas * atlas)
ImFontAtlasBuilder* builder = atlas->Builder;
// In theory we could decide to reduce the number of nodes, e.g. halve them, and waste a little texture space, but it doesn't seem worth it.
int pack_node_count = tex->Width;
const int pack_node_count = tex->Width;
builder->PackNodes.resize(pack_node_count);
IM_STATIC_ASSERT(sizeof(stbrp_context) <= sizeof(stbrp_context_opaque));
stbrp_init_target((stbrp_context*)(void*)&builder->PackContext, tex->Width, tex->Height, builder->PackNodes.Data, builder->PackNodes.Size);
@ -4046,38 +4112,40 @@ ImFontAtlasRect* ImFontAtlasPackGetRect(ImFontAtlas* atlas, ImFontAtlasRectId id
return &builder->Rects[index_entry->TargetIndex];
}
ImFontGlyph* ImFont::BuildLoadGlyph(ImWchar codepoint)
ImFontGlyph* ImFontBaked::BuildLoadGlyph(ImWchar codepoint)
{
ImFontAtlas* atlas = ContainerAtlas;
if (LockDisableLoading || atlas->Locked)
ImFont* font = ContainerFont;
ImFontBaked* baked = this;
ImFontAtlas* atlas = font->ContainerAtlas;
if (font->LockDisableLoading || atlas->Locked)
return NULL;
//char utf8_buf[5];
//IMGUI_DEBUG_LOG("[font] BuildAddGlyph U+%04X (%s)\n", (unsigned int)codepoint, ImTextCharToUtf8(utf8_buf, (unsigned int)codepoint));
// Load from single source or all sources?
int srcs_count = (LockSingleSrcConfigIdx != -1) ? 1 : SourcesCount;
ImFontConfig* srcs = (LockSingleSrcConfigIdx != -1) ? &Sources[LockSingleSrcConfigIdx] : Sources;
int srcs_count = (font->LockSingleSrcConfigIdx != -1) ? 1 : font->SourcesCount;
ImFontConfig* srcs = (font->LockSingleSrcConfigIdx != -1) ? &font->Sources[font->LockSingleSrcConfigIdx] : font->Sources;
// Call backend
const ImFontLoader* font_loader = atlas->FontLoader;
if (!font_loader->FontAddGlyph(atlas, this, srcs, srcs_count, codepoint))
if (!font_loader->FontBakedAddGlyph(atlas, baked, srcs, srcs_count, codepoint))
{
// Mark index as not found, so we don't attempt the search twice
BuildGrowIndex(codepoint + 1);
IndexAdvanceX[codepoint] = FallbackAdvanceX;
IndexLookup[codepoint] = IM_FONTGLYPH_INDEX_NOT_FOUND;
baked->BuildGrowIndex(codepoint + 1);
baked->IndexAdvanceX[codepoint] = baked->FallbackAdvanceX;
baked->IndexLookup[codepoint] = IM_FONTGLYPH_INDEX_NOT_FOUND;
return NULL;
}
// FIXME: Add hooks for e.g. #7962
ImFontGlyph* glyph = &Glyphs.back();
ImFontGlyph* glyph = &baked->Glyphs.back();
return glyph;
}
// The point of this indirection is to not be inlined in debug mode in order to not bloat inner loop.b
IM_MSVC_RUNTIME_CHECKS_OFF
static float BuildLoadGlyphGetAdvanceOrFallback(ImFont* font, unsigned int codepoint)
static float BuildLoadGlyphGetAdvanceOrFallback(ImFontBaked* font, unsigned int codepoint)
{
ImFontGlyph* glyph = font->BuildLoadGlyph((ImWchar)codepoint);
return glyph ? glyph->AdvanceX : font->FallbackAdvanceX;
@ -4124,9 +4192,7 @@ void ImFontAtlasDebugLogTextureRequests(ImFontAtlas* atlas)
struct ImGui_ImplStbTrueType_FontSrcData
{
stbtt_fontinfo FontInfo;
float ScaleForRasterX; // Factor in RasterizationDensity * OversampleH
float ScaleForRasterY; // Factor in RasterizationDensity * OversampleV
float ScaleForLayout;
float ScaleFactor;
};
static bool ImGui_ImplStbTrueType_FontSrcInit(ImFontAtlas* atlas, ImFontConfig* src)
@ -4152,28 +4218,10 @@ static bool ImGui_ImplStbTrueType_FontSrcInit(ImFontAtlas* atlas, ImFontConfig*
}
src->FontLoaderData = bd_font_data;
// FIXME-NEWFONTS: reevaluate sizing metrics
int oversample_h, oversample_v;
ImFontAtlasBuildGetOversampleFactors(src, &oversample_h, &oversample_v);
float scale;
if (src->SizePixels > 0.0f)
scale = stbtt_ScaleForPixelHeight(&bd_font_data->FontInfo, 1.0f);
bd_font_data->ScaleFactor = stbtt_ScaleForPixelHeight(&bd_font_data->FontInfo, 1.0f);
else
scale = -stbtt_ScaleForMappingEmToPixels(&bd_font_data->FontInfo, 1.0f);
bd_font_data->ScaleForRasterX = scale * src->SizePixels * src->RasterizerDensity * oversample_h;
bd_font_data->ScaleForRasterY = scale * src->SizePixels * src->RasterizerDensity * oversample_v;
bd_font_data->ScaleForLayout = scale * src->SizePixels;
// FIXME-NEWFONTS: make use of line gap value
int unscaled_ascent, unscaled_descent, unscaled_line_gap;
stbtt_GetFontVMetrics(&bd_font_data->FontInfo, &unscaled_ascent, &unscaled_descent, &unscaled_line_gap);
if (src->MergeMode == false)
{
ImFont* font = src->DstFont;
font->Ascent = ImCeil(unscaled_ascent * bd_font_data->ScaleForLayout);
font->Descent = ImFloor(unscaled_descent * bd_font_data->ScaleForLayout);
}
bd_font_data->ScaleFactor = -stbtt_ScaleForMappingEmToPixels(&bd_font_data->FontInfo, 1.0f);
return true;
}
@ -4197,7 +4245,27 @@ static bool ImGui_ImplStbTrueType_FontSrcContainsGlyph(ImFontAtlas* atlas, ImFon
return glyph_index != 0;
}
static bool ImGui_ImplStbTrueType_FontAddGlyph(ImFontAtlas* atlas, ImFont* font, ImFontConfig* srcs, int srcs_count, ImWchar codepoint)
static void ImGui_ImplStbTrueType_FontBakedInit(ImFontAtlas* atlas, ImFontBaked* baked)
{
IM_UNUSED(atlas);
ImFont* font = baked->ContainerFont;
for (int src_n = 0; src_n < font->SourcesCount; src_n++)
{
ImFontConfig* src = &font->Sources[src_n];
ImGui_ImplStbTrueType_FontSrcData* bd_font_data = (ImGui_ImplStbTrueType_FontSrcData*)src->FontLoaderData;
// FIXME-NEWFONTS: reevaluate how to use sizing metrics
// FIXME-NEWFONTS: make use of line gap value
float scale_for_layout = bd_font_data->ScaleFactor * baked->Size;
int unscaled_ascent, unscaled_descent, unscaled_line_gap;
stbtt_GetFontVMetrics(&bd_font_data->FontInfo, &unscaled_ascent, &unscaled_descent, &unscaled_line_gap);
baked->Ascent = ImCeil(unscaled_ascent * scale_for_layout);
baked->Descent = ImFloor(unscaled_descent * scale_for_layout);
}
}
static bool ImGui_ImplStbTrueType_FontBakedAddGlyph(ImFontAtlas* atlas, ImFontBaked* baked, ImFontConfig* srcs, int srcs_count, ImWchar codepoint)
{
// Search for first font which has the glyph
ImGui_ImplStbTrueType_FontSrcData* bd_font_data = NULL;
@ -4215,9 +4283,12 @@ static bool ImGui_ImplStbTrueType_FontAddGlyph(ImFontAtlas* atlas, ImFont* font,
if (glyph_index == 0)
return false; // Not found
const float scale_for_layout = bd_font_data->ScaleForLayout; // ~ (font units to pixels)
const float scale_for_raster_x = bd_font_data->ScaleForRasterX; // ~ (font units to pixels) * RasterizationDensity * OversampleH
const float scale_for_raster_y = bd_font_data->ScaleForRasterY; // ~ (font units to pixels) * RasterizationDensity * OversampleV
// Fonts unit to pixels
int oversample_h, oversample_v;
ImFontAtlasBuildGetOversampleFactors(src, baked->Size, &oversample_h, &oversample_v);
const float scale_for_layout = bd_font_data->ScaleFactor * baked->Size;
const float scale_for_raster_x = bd_font_data->ScaleFactor * baked->Size * src->RasterizerDensity * oversample_h;
const float scale_for_raster_y = bd_font_data->ScaleFactor * baked->Size * src->RasterizerDensity * oversample_v;
// Obtain size and advance
int x0, y0, x1, y1;
@ -4235,8 +4306,6 @@ static bool ImGui_ImplStbTrueType_FontAddGlyph(ImFontAtlas* atlas, ImFont* font,
// (generally based on stbtt_PackFontRangesRenderIntoRects)
if (is_visible)
{
int oversample_h, oversample_v;
ImFontAtlasBuildGetOversampleFactors(src, &oversample_h, &oversample_v);
const int w = (x1 - x0 + oversample_h - 1);
const int h = (y1 - y0 + oversample_v - 1);
ImFontAtlasRectId pack_id = ImFontAtlasPackAddRect(atlas, w, h);
@ -4246,9 +4315,7 @@ static bool ImGui_ImplStbTrueType_FontAddGlyph(ImFontAtlas* atlas, ImFont* font,
IM_ASSERT_USER_ERROR(pack_id >= 0, "Out of texture memory.");
return false;
}
ImFontAtlasRect* r = ImFontAtlasPackGetRect(atlas, pack_id);
font->MetricsTotalSurface += w * h;
// Render
stbtt_GetGlyphBitmapBox(&bd_font_data->FontInfo, glyph_index, scale_for_raster_x, scale_for_raster_y, &x0, &y0, &x1, &y1);
@ -4267,7 +4334,7 @@ static bool ImGui_ImplStbTrueType_FontAddGlyph(ImFontAtlas* atlas, ImFont* font,
stbtt__v_prefilter(bitmap_pixels, r->w, r->h, r->w, oversample_v);
float font_off_x = src->GlyphOffset.x + stbtt__oversample_shift(oversample_h);
float font_off_y = src->GlyphOffset.y + stbtt__oversample_shift(oversample_v) + IM_ROUND(font->Ascent);
float font_off_y = src->GlyphOffset.y + stbtt__oversample_shift(oversample_v) + IM_ROUND(baked->Ascent);
float recip_h = 1.0f / (oversample_h * src->RasterizerDensity);
float recip_v = 1.0f / (oversample_v * src->RasterizerDensity);
@ -4280,19 +4347,19 @@ static bool ImGui_ImplStbTrueType_FontAddGlyph(ImFontAtlas* atlas, ImFont* font,
glyph.Y1 = (y0 + (int)r->h) * recip_v + font_off_y;
glyph.Visible = true;
glyph.PackId = pack_id;
ImFontAtlasBuildAddFontGlyph(atlas, font, src, &glyph);
ImFontAtlasBakedAddFontGlyph(atlas, baked, src, &glyph);
// Copy to texture, post-process and queue update for backend
ImTextureData* tex = atlas->TexData;
IM_ASSERT(r->x + r->w <= tex->Width && r->y + r->h <= tex->Height);
ImFontAtlasTextureBlockConvert(bitmap_pixels, ImTextureFormat_Alpha8, w, tex->GetPixelsAt(r->x, r->y), tex->Format, tex->GetPitch(), w, h);
ImFontAtlasPostProcessData pp_data = { atlas, font, src, &font->Glyphs.back(), tex->GetPixelsAt(r->x, r->y), tex->Format, tex->GetPitch(), w, h };
ImFontAtlasPostProcessData pp_data = { atlas, baked->ContainerFont, src, baked, &baked->Glyphs.back(), tex->GetPixelsAt(r->x, r->y), tex->Format, tex->GetPitch(), w, h };
ImFontAtlasTextureBlockPostProcess(&pp_data);
ImFontAtlasTextureBlockQueueUpload(atlas, tex, r->x, r->y, r->w, r->h);
}
else
{
ImFontAtlasBuildAddFontGlyph(atlas, font, src, &glyph);
ImFontAtlasBakedAddFontGlyph(atlas, baked, src, &glyph);
}
return true;
@ -4305,7 +4372,9 @@ const ImFontLoader* ImFontAtlasGetFontLoaderForStbTruetype()
loader.FontSrcInit = ImGui_ImplStbTrueType_FontSrcInit;
loader.FontSrcDestroy = ImGui_ImplStbTrueType_FontSrcDestroy;
loader.FontSrcContainsGlyph = ImGui_ImplStbTrueType_FontSrcContainsGlyph;
loader.FontAddGlyph = ImGui_ImplStbTrueType_FontAddGlyph;
loader.FontBakedInit = ImGui_ImplStbTrueType_FontBakedInit;
loader.FontBakedDestroy = NULL;
loader.FontBakedAddGlyph = ImGui_ImplStbTrueType_FontBakedAddGlyph;
return &loader;
}
@ -4628,6 +4697,34 @@ void ImFontGlyphRangesBuilder::BuildRanges(ImVector<ImWchar>* out_ranges)
// [SECTION] ImFont
//-----------------------------------------------------------------------------
ImFontBaked::ImFontBaked()
{
memset(this, 0, sizeof(*this));
FallbackGlyphIndex = -1;
}
void ImFontBaked::ClearOutputData()
{
FallbackAdvanceX = 0.0f;
Glyphs.clear();
IndexAdvanceX.clear();
IndexLookup.clear();
FallbackGlyphIndex = -1;
Ascent = Descent = 0.0f;
MetricsTotalSurface = 0;
}
void ImFontBaked::BuildGrowIndex(int new_size)
{
IM_ASSERT(IndexAdvanceX.Size == IndexLookup.Size);
if (new_size <= IndexLookup.Size)
return;
//ImGuiContext& g = *GImGui;
//IMGUI_DEBUG_LOG_FONT("[font] BuildGrowIndex(%d)\n", new_size);
IndexAdvanceX.resize(new_size, -1.0f);
IndexLookup.resize(new_size, IM_FONTGLYPH_INDEX_UNUSED);
}
ImFont::ImFont()
{
memset(this, 0, sizeof(*this));
@ -4642,14 +4739,17 @@ ImFont::~ImFont()
void ImFont::ClearOutputData()
{
FallbackAdvanceX = 0.0f;
Glyphs.clear();
IndexAdvanceX.clear();
IndexLookup.clear();
FallbackGlyphIndex = -1;
Ascent = Descent = 0.0f;
MetricsTotalSurface = 0;
if (ImFontAtlas* atlas = ContainerAtlas)
if (ImFontAtlasBuilder* builder = atlas->Builder)
for (int baked_n = 0; baked_n < builder->BakedPool.Size; baked_n++)
{
ImFontBaked* baked = &builder->BakedPool[baked_n];
if (baked->ContainerFont == this)
ImFontAtlasBuildDiscardFontBaked(atlas, baked);
}
FallbackChar = EllipsisChar = 0;
memset(Used8kPagesMap, 0, sizeof(Used8kPagesMap));
LastBaked = NULL;
}
// API is designed this way to avoid exposing the 8K page size
@ -4665,24 +4765,15 @@ bool ImFont::IsGlyphRangeUnused(unsigned int c_begin, unsigned int c_last)
return true;
}
void ImFont::BuildGrowIndex(int new_size)
{
IM_ASSERT(IndexAdvanceX.Size == IndexLookup.Size);
if (new_size <= IndexLookup.Size)
return;
IndexAdvanceX.resize(new_size, -1.0f);
IndexLookup.resize(new_size, (ImU16)-1);
}
// x0/y0/x1/y1 are offset from the character upper-left layout position, in pixels. Therefore x0/y0 are often fairly close to zero.
// Not to be mistaken with texture coordinates, which are held by u0/v0/u1/v1 in normalized format (0.0..1.0 on each texture axis).
// 'src' is not necessarily == 'this->Sources' because multiple source fonts+configs can be used to build one target font.
ImFontGlyph* ImFontAtlasBuildAddFontGlyph(ImFontAtlas* atlas, ImFont* font, ImFontConfig* src, const ImFontGlyph* in_glyph)
ImFontGlyph* ImFontAtlasBakedAddFontGlyph(ImFontAtlas* atlas, ImFontBaked* baked, ImFontConfig* src, const ImFontGlyph* in_glyph)
{
int glyph_idx = font->Glyphs.Size;
font->Glyphs.push_back(*in_glyph);
ImFontGlyph& glyph = font->Glyphs[glyph_idx];
IM_ASSERT(font->Glyphs.Size < 0xFFFE); // IndexLookup[] hold 16-bit values and -1/-2 are reserved.
int glyph_idx = baked->Glyphs.Size;
baked->Glyphs.push_back(*in_glyph);
ImFontGlyph& glyph = baked->Glyphs[glyph_idx];
IM_ASSERT(baked->Glyphs.Size < 0xFFFE); // IndexLookup[] hold 16-bit values and -1/-2 are reserved.
// Set UV from packed rectangle
if (in_glyph->PackId >= 0)
@ -4693,6 +4784,7 @@ ImFontGlyph* ImFontAtlasBuildAddFontGlyph(ImFontAtlas* atlas, ImFont* font, ImFo
glyph.V0 = (r->y) * atlas->TexUvScale.y;
glyph.U1 = (r->x + r->w) * atlas->TexUvScale.x;
glyph.V1 = (r->y + r->h) * atlas->TexUvScale.y;
baked->MetricsTotalSurface += r->w * r->h;
}
if (src != NULL)
@ -4718,17 +4810,22 @@ ImFontGlyph* ImFontAtlasBuildAddFontGlyph(ImFontAtlas* atlas, ImFont* font, ImFo
// Update lookup tables
int codepoint = glyph.Codepoint;
font->BuildGrowIndex(codepoint + 1);
font->IndexAdvanceX[codepoint] = glyph.AdvanceX;
font->IndexLookup[codepoint] = (ImU16)glyph_idx;
baked->BuildGrowIndex(codepoint + 1);
baked->IndexAdvanceX[codepoint] = glyph.AdvanceX;
baked->IndexLookup[codepoint] = (ImU16)glyph_idx;
const int page_n = codepoint / 8192;
font->Used8kPagesMap[page_n >> 3] |= 1 << (page_n & 7);
baked->ContainerFont->Used8kPagesMap[page_n >> 3] |= 1 << (page_n & 7);
return &glyph;
}
void ImFont::AddRemapChar(ImWchar from_codepoint, ImWchar to_codepoint, bool overwrite_dst)
{
// FIXME-BAKED: Implement AddRemapChar()
IM_UNUSED(from_codepoint);
IM_UNUSED(to_codepoint);
IM_UNUSED(overwrite_dst);
/*
IM_ASSERT(IndexLookup.Size > 0); // Currently this can only be called AFTER the font has been built, aka after calling ImFontAtlas::GetTexDataAs*() function.
unsigned int index_size = (unsigned int)IndexLookup.Size;
@ -4740,10 +4837,11 @@ void ImFont::AddRemapChar(ImWchar from_codepoint, ImWchar to_codepoint, bool ove
BuildGrowIndex(from_codepoint + 1);
IndexLookup[from_codepoint] = (to_codepoint < index_size) ? IndexLookup.Data[to_codepoint] : (ImU16)-1;
IndexAdvanceX[from_codepoint] = (to_codepoint < index_size) ? IndexAdvanceX.Data[to_codepoint] : 1.0f;
*/
}
// Find glyph, load if necessary, return fallback if missing
ImFontGlyph* ImFont::FindGlyph(ImWchar c)
ImFontGlyph* ImFontBaked::FindGlyph(ImWchar c)
{
if (c < (size_t)IndexLookup.Size) IM_LIKELY
{
@ -4758,7 +4856,7 @@ ImFontGlyph* ImFont::FindGlyph(ImWchar c)
}
// Attempt to load but when missing, return NULL instead of FallbackGlyph
ImFontGlyph* ImFont::FindGlyphNoFallback(ImWchar c)
ImFontGlyph* ImFontBaked::FindGlyphNoFallback(ImWchar c)
{
if (c < (size_t)IndexLookup.Size) IM_LIKELY
{
@ -4768,11 +4866,10 @@ ImFontGlyph* ImFont::FindGlyphNoFallback(ImWchar c)
if (i != IM_FONTGLYPH_INDEX_UNUSED)
return &Glyphs.Data[i];
}
ImFontGlyph* glyph = BuildLoadGlyph(c);
return glyph;
return BuildLoadGlyph(c);
}
bool ImFont::IsGlyphLoaded(ImWchar c)
bool ImFontBaked::IsGlyphLoaded(ImWchar c)
{
if (c < (size_t)IndexLookup.Size) IM_LIKELY
{
@ -4797,7 +4894,7 @@ bool ImFont::IsGlyphInFont(ImWchar c)
// This is manually inlined in CalcTextSizeA() and CalcWordWrapPosition(), with a non-inline call to BuildLoadGlyphGetAdvanceOrFallback().
IM_MSVC_RUNTIME_CHECKS_OFF
float ImFont::GetCharAdvance(ImWchar c)
float ImFontBaked::GetCharAdvance(ImWchar c)
{
if ((int)c < IndexAdvanceX.Size)
{
@ -4813,6 +4910,46 @@ float ImFont::GetCharAdvance(ImWchar c)
}
IM_MSVC_RUNTIME_CHECKS_RESTORE
// ImFontBaked pointers are valid for the entire frame but shall never be kept between frames.
ImFontBaked* ImFont::GetFontBaked(float size)
{
ImFontBaked* baked = LastBaked;
if (baked && baked->Size == size)
return baked;
ImFontAtlas* atlas = ContainerAtlas;
ImFontAtlasBuilder* builder = atlas->Builder;
ImGuiID baked_id = ImFontAtlasBakedGetId(FontId, size);
ImFontBaked** p_baked_in_map = (ImFontBaked**)builder->BakedMap.GetVoidPtrRef(baked_id);
baked = *p_baked_in_map;
if (baked != NULL)
{
// FIXME-BAKED: Design for picking a nearest size?
IM_ASSERT(baked->Size == size && baked->ContainerFont == this && baked->BakedId == baked_id);
baked->LastUsedFrame = builder->FrameCount;
LastBaked = baked;
return baked;
}
// Create new
ImGuiContext& g = *GImGui;
IM_UNUSED(g); // for IMGUI_DEBUG_LOG_FONT()
IMGUI_DEBUG_LOG_FONT("[font] Created baked %.2fpx\n", size);
baked = builder->BakedPool.push_back(ImFontBaked());
baked->Size = size;
baked->BakedId = baked_id;
baked->ContainerFont = this;
baked->LastUsedFrame = ContainerAtlas->Builder->FrameCount;
LastBaked = baked;
*p_baked_in_map = baked; // To avoid 'builder->BakedMap.SetVoidPtr(baked_id, baked);' while we can.
if (atlas->FontLoader->FontBakedInit)
atlas->FontLoader->FontBakedInit(atlas, baked);
ImFontAtlasBuildSetupFontBakedSpecialGlyphs(atlas, baked->ContainerFont, baked);
return baked;
}
// Trim trailing space and find beginning of next line
static inline const char* CalcWordWrapNextLineStartA(const char* text, const char* text_end)
{
@ -4839,10 +4976,13 @@ const char* ImFont::CalcWordWrapPosition(float size, const char* text, const cha
// Cut words that cannot possibly fit within one line.
// e.g.: "The tropical fish" with ~5 characters worth of width --> "The tr" "opical" "fish"
ImFontBaked* baked = GetFontBaked(size);
const float scale = size / baked->Size;
float line_width = 0.0f;
float word_width = 0.0f;
float blank_width = 0.0f;
const float scale = size / FontSize;
wrap_width /= scale; // We work with unscaled widths to avoid scaling every characters
const char* word_end = text;
@ -4877,9 +5017,9 @@ const char* ImFont::CalcWordWrapPosition(float size, const char* text, const cha
}
// Optimized inline version of 'float char_width = GetCharAdvance((ImWchar)c);'
float char_width = (c < (unsigned int)IndexAdvanceX.Size) ? IndexAdvanceX.Data[c] : -1.0f;
float char_width = (c < (unsigned int)baked->IndexAdvanceX.Size) ? baked->IndexAdvanceX.Data[c] : -1.0f;
if (char_width < 0.0f)
char_width = BuildLoadGlyphGetAdvanceOrFallback(this, c);
char_width = BuildLoadGlyphGetAdvanceOrFallback(baked, c);
if (ImCharIsBlankW(c))
{
@ -4935,7 +5075,8 @@ ImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, cons
text_end = text_begin + ImStrlen(text_begin); // FIXME-OPT: Need to avoid this.
const float line_height = size;
const float scale = size / FontSize;
ImFontBaked* baked = GetFontBaked(size);
const float scale = size / baked->Size;
ImVec2 text_size = ImVec2(0, 0);
float line_width = 0.0f;
@ -4986,9 +5127,9 @@ ImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, cons
}
// Optimized inline version of 'float char_width = GetCharAdvance((ImWchar)c);'
float char_width = (c < (unsigned int)IndexAdvanceX.Size) ? IndexAdvanceX.Data[c] : -1.0f;
float char_width = (c < (unsigned int)baked->IndexAdvanceX.Size) ? baked->IndexAdvanceX.Data[c] : -1.0f;
if (char_width < 0.0f)
char_width = BuildLoadGlyphGetAdvanceOrFallback(this, c);
char_width = BuildLoadGlyphGetAdvanceOrFallback(baked, c);
char_width *= scale;
if (line_width + char_width >= max_width)
@ -5015,12 +5156,13 @@ ImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, cons
// Note: as with every ImDrawList drawing function, this expects that the font atlas texture is bound.
void ImFont::RenderChar(ImDrawList* draw_list, float size, const ImVec2& pos, ImU32 col, ImWchar c, const ImVec4* cpu_fine_clip)
{
const ImFontGlyph* glyph = FindGlyph(c);
ImFontBaked* baked = GetFontBaked(size);
const ImFontGlyph* glyph = baked->FindGlyph(c);
if (!glyph || !glyph->Visible)
return;
if (glyph->Colored)
col |= ~IM_COL32_A_MASK;
float scale = (size >= 0.0f) ? (size / FontSize) : 1.0f;
float scale = (size >= 0.0f) ? (size / baked->Size) : 1.0f;
float x = IM_TRUNC(pos.x);
float y = IM_TRUNC(pos.y);
@ -5062,8 +5204,10 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, Im
if (!text_end)
text_end = text_begin + ImStrlen(text_begin); // ImGui:: functions generally already provides a valid text_end, so this is merely to handle direct calls.
const float scale = size / FontSize;
const float line_height = FontSize * scale;
const float line_height = size;
ImFontBaked* baked = GetFontBaked(size);
const float scale = size / baked->Size;
const float origin_x = x;
const bool word_wrap_enabled = (wrap_width > 0.0f);
@ -5158,7 +5302,7 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, Im
continue;
}
const ImFontGlyph* glyph = FindGlyph((ImWchar)c);
const ImFontGlyph* glyph = baked->FindGlyph((ImWchar)c);
if (glyph == NULL)
continue;