WIP - Rasterizing ellipsis character from dot as one glyph + avoid preloading if it not needed.

This commit is contained in:
ocornut 2025-01-08 19:07:40 +01:00
parent ef28c35145
commit 95bcfc98b5
4 changed files with 75 additions and 23 deletions

View file

@ -3778,7 +3778,7 @@ void ImGui::RenderTextEllipsis(ImDrawList* draw_list, const ImVec2& pos_min, con
const float font_size = draw_list->_Data->FontSize;
const float font_scale = draw_list->_Data->FontScale;
const char* text_end_ellipsis = NULL;
const float ellipsis_width = font->EllipsisWidth * font_scale;
const float ellipsis_width = font->GetCharAdvance(font->EllipsisChar) * font_scale;
// We can now claim the space between pos_max.x and ellipsis_max.x
const float text_avail_width = ImMax((ImMax(pos_max.x, ellipsis_max_x) - ellipsis_width) - pos_min.x, 1.0f);
@ -3800,8 +3800,7 @@ void ImGui::RenderTextEllipsis(ImDrawList* draw_list, const ImVec2& pos_min, con
RenderTextClippedEx(draw_list, pos_min, ImVec2(clip_max_x, pos_max.y), text, text_end_ellipsis, &text_size, ImVec2(0.0f, 0.0f));
ImVec2 ellipsis_pos = ImTrunc(ImVec2(pos_min.x + text_size_clipped_x, pos_min.y));
if (ellipsis_pos.x + ellipsis_width <= ellipsis_max_x)
for (int i = 0; i < font->EllipsisCharCount; i++, ellipsis_pos.x += font->EllipsisCharStep * font_scale)
font->RenderChar(draw_list, font_size, ellipsis_pos, GetColorU32(ImGuiCol_Text), font->EllipsisChar);
font->RenderChar(draw_list, font_size, ellipsis_pos, GetColorU32(ImGuiCol_Text), font->EllipsisChar);
}
else
{

View file

@ -3745,13 +3745,10 @@ struct ImFont
// [Internal] Members: Cold ~32/40/60 bytes
// Conceptually Sources[] is the list of font sources merged to create this font.
short SourcesCount; // 2 // in // Number of ImFontConfig involved in creating this font. Usually 1, or >1 when merging multiple font sources into one ImFont.
short EllipsisCharCount; // 1 // out // 1 or 3
ImWchar EllipsisChar; // 2-4 // out // Character used for ellipsis rendering ('...').
ImWchar FallbackChar; // 2-4 // out // Character used if a glyph isn't found (U+FFFD, '?')
ImFontConfig* Sources; // 4-8 // in // Pointer within ContainerAtlas->Sources[], to SourcesCount instances
ImFontAtlas* ContainerAtlas; // 4-8 // out // What we has been loaded into
float EllipsisWidth; // 4 // out // Total ellipsis Width
float EllipsisCharStep; // 4 // out // Step between characters when EllipsisCount > 0
ImWchar EllipsisChar; // 2-4 // out // Character used for ellipsis rendering ('...').
ImWchar FallbackChar; // 2-4 // out // Character used if a glyph isn't found (U+FFFD, '?')
float Scale; // 4 // in // Base font scale (~1.0f), multiplied by the per-window font scale which you can adjust with SetWindowFontScale()
float Ascent, Descent; // 4+4 // out // Ascent: distance from top to bottom of e.g. 'A' [0..FontSize] (unscaled)
int MetricsTotalSurface;// 4 // out // Total surface in pixels to get an idea of the font rasterization/texture cost (not exact, we approximate the cost of padding between glyphs)

View file

@ -2464,6 +2464,7 @@ void ImTextureData::DestroyPixels()
// - ImFontAtlasTextureBlockConvert()
// - ImFontAtlasTextureBlockPostProcess()
// - ImFontAtlasTextureBlockPostProcessMultiply()
// - ImFontAtlasTextureBlockFill()
// - ImFontAtlasTextureBlockCopy()
// - ImFontAtlasTextureBlockQueueUpload()
//-----------------------------------------------------------------------------
@ -2492,6 +2493,7 @@ void ImTextureData::DestroyPixels()
// - ImFontAtlasBuildUpdateBasicTexData()
// - ImFontAtlasBuildUpdateLinesTexData()
// - ImFontAtlasBuildAddFont()
// - ImFontAtlasBuildSetupFontCreateEllipsisFromDot()
// - ImFontAtlasBuildSetupFontSpecialGlyphs()
// - ImFontAtlasBuildReloadFont()
//-----------------------------------------------------------------------------
@ -2823,10 +2825,29 @@ void ImFontAtlasTextureBlockPostProcessMultiply(ImFontAtlasPostProcessData* data
}
}
// Convert block from one texture to another
// 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)
{
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
{
for (int y = 0; y < h; y++)
{
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 != dst_tex);
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);
@ -3486,6 +3507,44 @@ bool ImFontAtlasBuildAddFont(ImFontAtlas* atlas, ImFontConfig* src)
return true;
}
// 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 FontBackend_FontAddGlyph() and suggest that we should add further helpers.
static void ImFontAtlasBuildSetupFontCreateEllipsisFromDot(ImFontAtlas* atlas, ImFontConfig* cfg, const ImFontGlyph* dot_glyph)
{
ImFont* font = cfg->DstFont;
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.
glyph.AdvanceX = ImMax(dot_glyph->AdvanceX, dot_glyph->X0 + dot_step * 3.0f - dot_spacing); // FIXME: Slightly odd for normally mono-space fonts but since this is used for trailing contents.
glyph.X0 = dot_glyph->X0;
glyph.Y0 = dot_glyph->Y0;
glyph.X1 = dot_glyph->X0 + dot_step * 3 - dot_spacing;
glyph.Y1 = dot_glyph->Y1;
glyph.U0 = (r->x) * atlas->TexUvScale.x;
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;
glyph.Visible = true;
glyph.PackId = pack_id;
font->BuildRegisterGlyph(cfg, &glyph);
font->EllipsisChar = (ImWchar)glyph.Codepoint;
// Copy to texture, post-process and queue update for backend
// FIXME-NEWATLAS-V2: Dot glyph is already post-processed as this point, so this would damage it.
ImTextureData* tex = atlas->TexData;
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);
}
// Load/identify special glyphs
// (note that this is called again for fonts with MergeMode)
void ImFontAtlasBuildSetupFontSpecialGlyphs(ImFontAtlas* atlas, ImFontConfig* src)
@ -3529,23 +3588,19 @@ void ImFontAtlasBuildSetupFontSpecialGlyphs(ImFontAtlas* atlas, ImFontConfig* sr
// FIXME: Note that 0x2026 is rarely included in our font ranges. Because of this we are more likely to use three individual dots.
const ImWchar ellipsis_chars[] = { src->EllipsisChar, (ImWchar)0x2026, (ImWchar)0x0085 };
if (font->EllipsisChar == 0)
if (const ImFontGlyph* glyph = LoadFirstExistingGlyph(font, ellipsis_chars, IM_ARRAYSIZE(ellipsis_chars)))
{
font->EllipsisChar = (ImWchar)glyph->Codepoint;
font->EllipsisCharCount = 1;
font->EllipsisWidth = font->EllipsisCharStep = glyph->X1;
}
for (ImWchar candidate_char : ellipsis_chars)
if (candidate_char != 0 && font->IsGlyphInFont(candidate_char))
{
font->EllipsisChar = candidate_char;
break;
}
if (font->EllipsisChar == 0)
{
// FIXME-NEWATLAS-V2: We can now rasterize this into a regular character and register it!
const ImWchar dots_chars[] = { (ImWchar)'.', (ImWchar)0xFF0E };
if (const ImFontGlyph* dot_glyph = LoadFirstExistingGlyph(font, dots_chars, IM_ARRAYSIZE(dots_chars)))
{
font->EllipsisChar = (ImWchar)dot_glyph->Codepoint;
font->EllipsisCharCount = 3;
font->EllipsisCharStep = (float)(int)(dot_glyph->X1 - dot_glyph->X0) + 1.0f;
font->EllipsisWidth = ImMax(dot_glyph->AdvanceX, dot_glyph->X0 + font->EllipsisCharStep * 3.0f - 1.0f); // FIXME: Slightly odd for normally mono-space fonts but since this is used for trailing contents.
}
ImFontAtlasBuildSetupFontCreateEllipsisFromDot(atlas, src, dot_glyph);
else
font->EllipsisChar = (ImWchar)' ';
}
font->LockSingleSrcConfigIdx = -1;
}

View file

@ -4022,6 +4022,7 @@ IMGUI_API void ImFontAtlasUpdateNewFrame(ImFontAtlas* atlas);
IMGUI_API 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);
IMGUI_API void ImFontAtlasTextureBlockPostProcess(ImFontAtlasPostProcessData* data);
IMGUI_API void ImFontAtlasTextureBlockPostProcessMultiply(ImFontAtlasPostProcessData* data, float multiply_factor);
IMGUI_API void ImFontAtlasTextureBlockFill(ImTextureData* dst_tex, int dst_x, int dst_y, int w, int h, ImU32 col);
IMGUI_API void ImFontAtlasTextureBlockCopy(ImTextureData* src_tex, int src_x, int src_y, ImTextureData* dst_tex, int dst_x, int dst_y, int w, int h);
IMGUI_API void ImFontAtlasTextureBlockQueueUpload(ImFontAtlas* atlas, ImTextureData* tex, int x, int y, int w, int h);