Fonts: encode additional data in ImFontAtlasRectId to detect invalid id + added Rects debug browser.

This commit is contained in:
ocornut
2025-04-11 17:16:06 +02:00
parent 0436fba13c
commit ed2bb2cff0
4 changed files with 94 additions and 17 deletions

View File

@@ -3238,7 +3238,8 @@ ImFontAtlasRectId ImFontAtlas::AddCustomRect(int width, int height, ImFontAtlasR
void ImFontAtlas::RemoveCustomRect(ImFontAtlasRectId id)
{
IM_ASSERT(id != ImFontAtlasRectId_Invalid);
if (ImFontAtlasPackGetRectSafe(this, id) == NULL)
return;
ImFontAtlasPackDiscardRect(this, id);
}
@@ -3294,10 +3295,12 @@ int ImFontAtlas::AddCustomRectFontGlyphForSize(ImFont* font, float font_size, Im
bool ImFontAtlas::GetCustomRect(ImFontAtlasRectId id, ImFontAtlasRect* out_r) const
{
ImTextureRect* r = ImFontAtlasPackGetRect((ImFontAtlas*)this, id);
ImTextureRect* r = ImFontAtlasPackGetRectSafe((ImFontAtlas*)this, id);
if (r == NULL)
return false;
IM_ASSERT(TexData->Width > 0 && TexData->Height > 0); // Font atlas needs to be built before we can calculate UV coordinates
if (out_r == NULL)
return true;
out_r->x = r->x;
out_r->y = r->y;
out_r->w = r->w;
@@ -4001,7 +4004,7 @@ void ImFontAtlasBuildRepackTexture(ImFontAtlas* atlas, int w, int h)
ImFontAtlasBuildGrowTexture(atlas, w, h); // Recurse
return;
}
IM_ASSERT(new_r_id == builder->RectsIndex.index_from_ptr(&index_entry));
IM_ASSERT(ImFontAtlasRectId_GetIndex(new_r_id) == builder->RectsIndex.index_from_ptr(&index_entry));
ImTextureRect* new_r = ImFontAtlasPackGetRect(atlas, new_r_id);
ImFontAtlasTextureBlockCopy(old_tex, old_r.x, old_r.y, new_tex, new_r->x, new_r->y, new_r->w, new_r->h);
}
@@ -4230,17 +4233,18 @@ static ImFontAtlasRectId ImFontAtlasPackAllocRectEntry(ImFontAtlas* atlas, int r
builder->RectsIndex.resize(builder->RectsIndex.Size + 1);
index_idx = builder->RectsIndex.Size - 1;
index_entry = &builder->RectsIndex[index_idx];
memset(index_entry, 0, sizeof(*index_entry));
}
else
{
index_idx = builder->RectsIndexFreeListStart;
index_entry = &builder->RectsIndex[index_idx];
IM_ASSERT(index_entry->IsUsed == false);
IM_ASSERT(index_entry->IsUsed == false && index_entry->Generation > 0); // Generation is incremented during DiscardRect
builder->RectsIndexFreeListStart = index_entry->TargetIndex;
}
index_entry->TargetIndex = rect_idx;
index_entry->IsUsed = 1;
return (ImFontAtlasRectId)index_idx;
return ImFontAtlasRectId_Make(index_idx, index_entry->Generation);
}
// Overwrite existing entry
@@ -4248,23 +4252,29 @@ static ImFontAtlasRectId ImFontAtlasPackReuseRectEntry(ImFontAtlas* atlas, ImFon
{
IM_ASSERT(index_entry->IsUsed);
index_entry->TargetIndex = atlas->Builder->Rects.Size - 1;
return atlas->Builder->RectsIndex.index_from_ptr(index_entry);
int index_idx = atlas->Builder->RectsIndex.index_from_ptr(index_entry);
return ImFontAtlasRectId_Make(index_idx, index_entry->Generation);
}
// This is expected to be called in batches and followed by a repack
void ImFontAtlasPackDiscardRect(ImFontAtlas* atlas, ImFontAtlasRectId id)
{
IM_ASSERT(id != ImFontAtlasRectId_Invalid);
ImFontAtlasBuilder* builder = (ImFontAtlasBuilder*)atlas->Builder;
ImFontAtlasRectEntry* index_entry = &builder->RectsIndex[id];
IM_ASSERT(index_entry->IsUsed && index_entry->TargetIndex >= 0);
ImTextureRect* rect = ImFontAtlasPackGetRect(atlas, id);
if (rect == NULL)
return;
ImFontAtlasBuilder* builder = atlas->Builder;
int index_idx = ImFontAtlasRectId_GetIndex(id);
ImFontAtlasRectEntry* index_entry = &builder->RectsIndex[index_idx];
IM_ASSERT(index_entry->IsUsed && index_entry->TargetIndex >= 0);
index_entry->IsUsed = false;
index_entry->TargetIndex = builder->RectsIndexFreeListStart;
index_entry->Generation++;
const int pack_padding = atlas->TexGlyphPadding;
builder->RectsIndexFreeListStart = id;
builder->RectsIndexFreeListStart = index_idx;
builder->RectsDiscardedCount++;
builder->RectsDiscardedSurface += (rect->w + pack_padding) * (rect->h + pack_padding);
rect->w = rect->h = 0; // Clear rectangle so it won't be packed again
@@ -4319,16 +4329,36 @@ ImFontAtlasRectId ImFontAtlasPackAddRect(ImFontAtlas* atlas, int w, int h, ImFon
return ImFontAtlasPackAllocRectEntry(atlas, builder->Rects.Size - 1);
}
// Important: return pointer is valid until next call to AddRect(), e.g. FindGlyph(), CalcTextSize() can all potentially invalidate previous pointers.
// Generally for non-user facing functions: assert on invalid ID.
ImTextureRect* ImFontAtlasPackGetRect(ImFontAtlas* atlas, ImFontAtlasRectId id)
{
IM_ASSERT(id != ImFontAtlasRectId_Invalid);
int index_idx = ImFontAtlasRectId_GetIndex(id);
int generation = ImFontAtlasRectId_GetGeneration(id);
ImFontAtlasBuilder* builder = (ImFontAtlasBuilder*)atlas->Builder;
ImFontAtlasRectEntry* index_entry = &builder->RectsIndex[id];
ImFontAtlasRectEntry* index_entry = &builder->RectsIndex[index_idx];
IM_ASSERT(index_entry->Generation == generation);
IM_ASSERT(index_entry->IsUsed);
return &builder->Rects[index_entry->TargetIndex];
}
// For user-facing functions: return NULL on invalid ID.
// Important: return pointer is valid until next call to AddRect(), e.g. FindGlyph(), CalcTextSize() can all potentially invalidate previous pointers.
ImTextureRect* ImFontAtlasPackGetRectSafe(ImFontAtlas* atlas, ImFontAtlasRectId id)
{
if (id == ImFontAtlasRectId_Invalid)
return NULL;
int index_idx = ImFontAtlasRectId_GetIndex(id);
int generation = ImFontAtlasRectId_GetGeneration(id);
ImFontAtlasBuilder* builder = (ImFontAtlasBuilder*)atlas->Builder;
if (index_idx >= builder->RectsIndex.Size)
return NULL;
ImFontAtlasRectEntry* index_entry = &builder->RectsIndex[index_idx];
if (index_entry->Generation != generation || !index_entry->IsUsed)
return NULL;
return &builder->Rects[index_entry->TargetIndex];
}
// Important! This assume by ImFontConfig::GlyphFilter is a SMALL ARRAY (e.g. <10 entries)
static bool ImFontAtlasBuildAcceptCodepointForSource(ImFontConfig* src, ImWchar codepoint)
{