diff --git a/docs/FONTS.md b/docs/FONTS.md index ce08f176d903..2b4ae7409a14 100644 --- a/docs/FONTS.md +++ b/docs/FONTS.md @@ -147,7 +147,7 @@ ImFont* font = io.Fonts->AddFontFromFileTTF("font.ttf", size_pixels, &config); // Load a first font ImFont* font = io.Fonts->AddFontDefault(); ImFontConfig config; -config.MergeMode = true; +config.MergeTarget = font; io.Fonts->AddFontFromFileTTF("DroidSans.ttf", 0.0f, &config); // Merge into first font to add e.g. Asian characters io.Fonts->AddFontFromFileTTF("fontawesome-webfont.ttf", 0.0f, &config); // Merge into first font to add Icons io.Fonts->Build(); @@ -162,7 +162,7 @@ ImFont* font = io.Fonts->AddFontDefault(); // so ensure it is available at the time of building or calling GetTexDataAsRGBA32(). static const ImWchar icons_ranges[] = { 0xf000, 0xf3ff, 0 }; // Will not be copied by AddFont* so keep in scope. ImFontConfig config; -config.MergeMode = true; +config.MergeTarget = font; io.Fonts->AddFontFromFileTTF("DroidSans.ttf", 18.0f, &config, io.Fonts->GetGlyphRangesJapanese()); // Merge into first font io.Fonts->AddFontFromFileTTF("fontawesome-webfont.ttf", 18.0f, &config, icons_ranges); // Merge into first font io.Fonts->Build(); @@ -269,9 +269,9 @@ Example Setup: // Merge icons into default tool font #include "IconsFontAwesome.h" ImGuiIO& io = ImGui::GetIO(); -io.Fonts->AddFontDefault(); +ImFont *font = io.Fonts->AddFontDefault(); ImFontConfig config; -config.MergeMode = true; +config.MergeTarget = font; config.GlyphMinAdvanceX = 13.0f; // Use if you want to make the icon monospaced io.Fonts->AddFontFromFileTTF("fonts/fontawesome-webfont.ttf", 13.0f, &config); ``` @@ -280,10 +280,10 @@ io.Fonts->AddFontFromFileTTF("fonts/fontawesome-webfont.ttf", 13.0f, &config); // Merge icons into default tool font #include "IconsFontAwesome.h" ImGuiIO& io = ImGui::GetIO(); -io.Fonts->AddFontDefault(); +ImFont *font = io.Fonts->AddFontDefault(); ImFontConfig config; -config.MergeMode = true; +config.MergeTarget = font; config.GlyphMinAdvanceX = 13.0f; // Use if you want to make the icon monospaced static const ImWchar icon_ranges[] = { ICON_MIN_FA, ICON_MAX_FA, 0 }; io.Fonts->AddFontFromFileTTF("fonts/fontawesome-webfont.ttf", 13.0f, &config, icon_ranges); @@ -322,11 +322,11 @@ Here's an application using icons ("Avoyd", https://www.avoyd.com): static ImWchar exclude_ranges[] = { ICON_MIN_FA, ICON_MAX_FA, 0 }; ImFontConfig cfg1; cfg1.GlyphExcludeRanges = exclude_ranges; -io.Fonts->AddFontFromFileTTF("segoeui.ttf", 0.0f, &cfg1); +ImFont *font1 = io.Fonts->AddFontFromFileTTF("segoeui.ttf", 0.0f, &cfg1); // Add Font Source 2, which expects to use the range above ImFontConfig cfg2; -cfg2.MergeMode = true; +cfg2.MergeTarget = font1; io.Fonts->AddFontFromFileTTF("FontAwesome4.ttf", 0.0f, &cfg2); ``` Another (silly) example: @@ -335,11 +335,11 @@ Another (silly) example: static ImWchar exclude_ranges[] = { 'A', 'Z', 0 }; ImFontConfig cfg1; cfg1.GlyphExcludeRanges = exclude_ranges; -io.Fonts->AddFontFromFileTTF("segoeui.ttf", 0.0f, &cfg1); +ImFont *font1 = io.Fonts->AddFontFromFileTTF("segoeui.ttf", 0.0f, &cfg1); // Load another font to fill the gaps ImFontConfig cfg2; -cfg2.MergeMode = true; +cfg2.MergeTarget = font1; io.Fonts->AddFontFromFileTTF("Roboto-Medium.ttf", 0.0f, &cfg2); ``` ![image](https://github.com/user-attachments/assets/f3d751d3-1aee-4698-bd9b-f511902f56bb) @@ -373,10 +373,10 @@ You can use `Metrics/Debugger->Fonts->Font->Input Glyphs Overlap Detection Tool` ![colored glyphs](https://user-images.githubusercontent.com/8225057/106171241-9dc4ba80-6191-11eb-8a69-ca1467b206d1.png) ```cpp -io.Fonts->AddFontFromFileTTF("../../../imgui_dev/data/fonts/NotoSans-Regular.ttf", 16.0f); +ImFont *font = io.Fonts->AddFontFromFileTTF("../../../imgui_dev/data/fonts/NotoSans-Regular.ttf", 16.0f); static ImWchar ranges[] = { 0x1, 0x1FFFF, 0 }; static ImFontConfig cfg; -cfg.MergeMode = true; +cfg.MergeTarget = font; cfg.FontLoaderFlags |= ImGuiFreeTypeLoaderFlags_LoadColor; io.Fonts->AddFontFromFileTTF("C:\\Windows\\Fonts\\seguiemj.ttf", 16.0f, &cfg); ``` diff --git a/imgui.h b/imgui.h index 4caa4e134cd4..2d4ff1897e3d 100644 --- a/imgui.h +++ b/imgui.h @@ -3474,7 +3474,7 @@ struct ImFontConfig bool FontDataOwnedByAtlas; // true // TTF/OTF data ownership taken by the container ImFontAtlas (will delete memory itself). // Options - bool MergeMode; // false // Merge into previous ImFont, so you can combine multiple inputs font into one ImFont (e.g. ASCII font + icons + Japanese glyphs). You may want to use GlyphOffset.y when merge font of different heights. + ImFont* MergeTarget; // NULL // Merge into specified ImFont, so you can combine multiple inputs font into one ImFont (e.g. ASCII font + icons + Japanese glyphs). You may want to use GlyphOffset.y when merge font of different heights. bool PixelSnapH; // false // Align every glyph AdvanceX to pixel boundaries. Useful e.g. if you are merging a non-pixel aligned font with the default font. If enabled, you can set OversampleH/V to 1. bool PixelSnapV; // true // Align Scaled GlyphOffset.y to pixel boundaries. ImS8 OversampleH; // 0 (2) // Rasterize at higher quality for sub-pixel positioning. 0 == auto == 1 or 2 depending on size. Note the difference between 2 and 3 is minimal. You can reduce this to 1 for large glyphs save memory. Read https://github.com/nothings/stb/blob/master/tests/oversample/README.md for details. @@ -3499,6 +3499,7 @@ struct ImFontConfig ImFont* DstFont; // Target font (as we merging fonts, multiple ImFontConfig may target the same font) const ImFontLoader* FontLoader; // Custom font backend for this source (default source is the one stored in ImFontAtlas) void* FontLoaderData; // Font loader opaque storage (per font config) + void* UserData; IMGUI_API ImFontConfig(); }; diff --git a/imgui_draw.cpp b/imgui_draw.cpp index ed6a91ab3b2f..d485866922d9 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -3012,7 +3012,7 @@ ImFont* ImFontAtlas::AddFont(const ImFontConfig* font_cfg_in) // Create new font ImFont* font; - if (!font_cfg_in->MergeMode) + if (!font_cfg_in->MergeTarget) { font = IM_NEW(ImFont)(); font->FontId = FontNextUniqueID++; @@ -3023,8 +3023,8 @@ ImFont* ImFontAtlas::AddFont(const ImFontConfig* font_cfg_in) } 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(); + IM_ASSERT(font_cfg_in->MergeTarget->ContainerAtlas == this); + font = font_cfg_in->MergeTarget; } // Add to list @@ -3064,7 +3064,7 @@ ImFont* ImFontAtlas::AddFont(const ImFontConfig* font_cfg_in) ImFontAtlasFontDestroySourceData(this, font_cfg); Sources.pop_back(); font->Sources.pop_back(); - if (!font_cfg->MergeMode) + if (!font_cfg->MergeTarget) { IM_DELETE(font); Fonts.pop_back(); @@ -3610,7 +3610,7 @@ bool ImFontAtlasFontSourceInit(ImFontAtlas* atlas, ImFontConfig* src) void ImFontAtlasFontSourceAddToFont(ImFontAtlas* atlas, ImFont* font, ImFontConfig* src) { - if (src->MergeMode == false) + if (!src->MergeTarget) { font->ClearOutputData(); //font->FontSize = src->SizePixels; @@ -3718,7 +3718,7 @@ static void ImFontAtlasBuildSetupFontBakedBlanks(ImFontAtlas* atlas, ImFontBaked } // Load/identify special glyphs -// (note that this is called again for fonts with MergeMode) +// (note that this is called again for fonts with MergeTarget) void ImFontAtlasBuildSetupFontSpecialGlyphs(ImFontAtlas* atlas, ImFont* font, ImFontConfig* src) { IM_UNUSED(atlas); @@ -4436,11 +4436,14 @@ static ImFontGlyph* ImFontBaked_BuildLoadGlyph(ImFontBaked* baked, ImWchar codep return glyph; // Call backend - char* loader_user_data_p = (char*)baked->FontLoaderDatas; - int src_n = 0; - for (ImFontConfig* src : font->Sources) + bool no_fallback = false; + size_t loader_user_data_n = 0; + auto loadGlyph = [=, &no_fallback, &loader_user_data_n](int src_n) -> ImFontGlyph* { + ImFontConfig* src = font->Sources[src_n]; const ImFontLoader* loader = src->FontLoader ? src->FontLoader : atlas->FontLoader; + void *loader_user_data_p = (char*)baked->FontLoaderDatas + loader_user_data_n; + loader_user_data_n += loader->FontBakedSrcLoaderDataSize; if (!src->GlyphExcludeRanges || ImFontAtlasBuildAcceptCodepointForSource(src, codepoint)) { if (only_load_advance_x == NULL) @@ -4460,12 +4463,57 @@ static ImFontGlyph* ImFontBaked_BuildLoadGlyph(ImFontBaked* baked, ImWchar codep if (loader->FontBakedLoadGlyph(atlas, src, baked, loader_user_data_p, codepoint, NULL, only_load_advance_x)) { ImFontAtlasBakedAddFontGlyphAdvancedX(atlas, baked, src, codepoint, *only_load_advance_x); + no_fallback = true; return NULL; } } } - loader_user_data_p += loader->FontBakedSrcLoaderDataSize; - src_n++; + + return NULL; + }; + + int src_n = 0; + for (; src_n < font->Sources.Size; ++src_n) + { + ImFontGlyph* glyph = loadGlyph(src_n); + if (glyph || no_fallback) + return glyph; + } + + if (atlas->FontLoader->FontAddFallbackSrc) + { + atlas->FontLoader->FontAddFallbackSrc(atlas, font, codepoint); + + for (; src_n < font->Sources.Size; ++src_n) + { + ImFontConfig* src = font->Sources[src_n]; + const ImFontLoader* loader = src->FontLoader ? src->FontLoader : atlas->FontLoader; + + for (int baked_n = 0; baked_n < atlas->Builder->BakedPool.Size; baked_n++) + { + ImFontBaked* baked_sibling = &atlas->Builder->BakedPool[baked_n]; + if (baked_sibling->ContainerFont != font || baked_sibling->WantDestroy) + continue; + + if (loader->FontBakedSrcLoaderDataSize > 0) + { + void *new_data = IM_ALLOC(loader_user_data_n + loader->FontBakedSrcLoaderDataSize); + memcpy(new_data, baked_sibling->FontLoaderDatas, loader_user_data_n); + IM_FREE(baked_sibling->FontLoaderDatas); + baked_sibling->FontLoaderDatas = new_data; + } + + if (loader->FontBakedInit) + { + void *loader_user_data_p = (char*)baked_sibling->FontLoaderDatas + loader_user_data_n; + loader->FontBakedInit(atlas, src, baked_sibling, loader_user_data_p); + } + } + + ImFontGlyph* glyph = loadGlyph(src_n); + if (glyph || no_fallback) + return glyph; + } } // Lazily load fallback glyph @@ -4572,14 +4620,14 @@ static bool ImGui_ImplStbTrueType_FontSrcInit(ImFontAtlas* atlas, ImFontConfig* } src->FontLoaderData = bd_font_data; - if (src->MergeMode && src->SizePixels == 0.0f) + if (src->MergeTarget && src->SizePixels == 0.0f) src->SizePixels = src->DstFont->Sources[0]->SizePixels; if (src->SizePixels >= 0.0f) bd_font_data->ScaleFactor = stbtt_ScaleForPixelHeight(&bd_font_data->FontInfo, 1.0f); else bd_font_data->ScaleFactor = stbtt_ScaleForMappingEmToPixels(&bd_font_data->FontInfo, 1.0f); - if (src->MergeMode && src->SizePixels != 0.0f) + if (src->MergeTarget && src->SizePixels != 0.0f) bd_font_data->ScaleFactor *= src->SizePixels / src->DstFont->Sources[0]->SizePixels; // FIXME-NEWATLAS: Should tidy up that a bit return true; @@ -4609,7 +4657,7 @@ static bool ImGui_ImplStbTrueType_FontBakedInit(ImFontAtlas* atlas, ImFontConfig IM_UNUSED(atlas); ImGui_ImplStbTrueType_FontSrcData* bd_font_data = (ImGui_ImplStbTrueType_FontSrcData*)src->FontLoaderData; - if (src->MergeMode == false) + if (!src->MergeTarget) { // FIXME-NEWFONTS: reevaluate how to use sizing metrics // FIXME-NEWFONTS: make use of line gap value diff --git a/imgui_internal.h b/imgui_internal.h index e92ab7b5c709..7a83659a2e10 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -3700,6 +3700,7 @@ struct ImFontLoader bool (*FontBakedInit)(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void* loader_data_for_baked_src); void (*FontBakedDestroy)(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void* loader_data_for_baked_src); bool (*FontBakedLoadGlyph)(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void* loader_data_for_baked_src, ImWchar codepoint, ImFontGlyph* out_glyph, float* out_advance_x); + void (*FontAddFallbackSrc)(ImFontAtlas* atlas, ImFont* parent, ImWchar codepoint); // merges zero or more new font sources into the parent ImFont which should contain a glyph for the codepoint // Size of backend data, Per Baked * Per Source. Buffers are managed by core to avoid excessive allocations. // FIXME: At this point the two other types of buffers may be managed by core to be consistent? diff --git a/misc/freetype/imgui_freetype.cpp b/misc/freetype/imgui_freetype.cpp index 7a9468375505..e01b721ad378 100644 --- a/misc/freetype/imgui_freetype.cpp +++ b/misc/freetype/imgui_freetype.cpp @@ -422,7 +422,7 @@ bool ImGui_ImplFreeType_FontBakedInit(ImFontAtlas* atlas, ImFontConfig* src, ImF { IM_UNUSED(atlas); float size = baked->Size; - if (src->MergeMode && src->SizePixels != 0.0f) + if (src->MergeTarget && src->SizePixels != 0.0f) size *= (src->SizePixels / baked->ContainerFont->Sources[0]->SizePixels); ImGui_ImplFreeType_FontSrcData* bd_font_data = (ImGui_ImplFreeType_FontSrcData*)src->FontLoaderData; @@ -450,7 +450,7 @@ bool ImGui_ImplFreeType_FontBakedInit(ImFontAtlas* atlas, ImFontConfig* src, ImF FT_Request_Size(bd_font_data->FtFace, &req); // Output - if (src->MergeMode == false) + if (!src->MergeTarget) { // Read metrics FT_Size_Metrics metrics = bd_baked_data->FtSize->metrics;