ImGuiManager: Move glyph ranges to separate file

This commit is contained in:
Stenzek 2024-08-25 22:52:21 +10:00
parent 7fdb4317d8
commit 307f3bb9e3
No known key found for this signature in database
5 changed files with 139 additions and 40 deletions

View File

@ -0,0 +1,29 @@
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
// SPDX-License-Identifier: (GPL-3.0 OR PolyForm-Strict-1.0.0)
#pragma once
// To generate: "<your emoji>".encode("utf-8") in Python 3.
// List to use: https://www.freecodecamp.org/news/all-emojis-emoji-list-for-copy-and-paste/
#define ICON_EMOJI_WARNING "\xe2\x9a\xa0"
#define ICON_EMOJI_OPTICAL_DISK "\xf0\x9f\x92\xbf"
#define ICON_EMOJI_FLOPPY_DISK "\xf0\x9f\x92\xbe"
#define ICON_EMOJI_INFORMATION "\xe2\x84\xb9"
#define ICON_EMOJI_FAST_FORWARD "\xe2\x8f\xa9"
#define ICON_EMOJI_FAST_REVERSE "\xe2\x8f\xaa"
#define ICON_EMOJI_RECORD "\xe2\x8f\xba"
#define ICON_EMOJI_PAUSE "\xe2\x8f\xb8"
#define ICON_EMOJI_CAMERA "\xf0\x9f\x93\xb7"
#define ICON_EMOJI_CAMERA_WITH_FLASH "\xf0\x9f\x93\xb8"
#define ICON_EMOJI_MOVIE_CAMERA "\xf0\x9f\x8e\xa5"
#define ICON_EMOJI_MOUSE "\xf0\x9f\x96\xb1"
#define ICON_EMOJI_MUTED_SPEAKER "\xf0\x9f\x94\x87"
#define ICON_EMOJI_LOW_VOLUME_SPEAKER "\xf0\x9f\x94\x88"
#define ICON_EMOJI_MEDIUM_VOLUME_SPEAKER "\xf0\x9f\x94\x89"
#define ICON_EMOJI_HIGH_VOLUME_SPEAKER "\xf0\x9f\x94\x8a"
#define ICON_EMOJI_FILE_FOLDER "\xf0\x9f\x93\x81"
#define ICON_EMOJI_OPEN_THE_FOLDER "\xf0\x9f\x93\x82"
#define ICON_EMOJI_MAGNIFIYING_GLASS_TILTED_LEFT "\xf0\x9f\x94\x8d"
#define ICON_EMOJI_LOCKED "\xf0\x9f\x94\x92"
#define ICON_EMOJI_UNLOCKED "\xf0\x9f\x94\x93"

View File

@ -10,7 +10,8 @@ import re
src_dir = os.path.join(os.path.dirname(__file__), "..", "src") src_dir = os.path.join(os.path.dirname(__file__), "..", "src")
fa_file = os.path.join(os.path.dirname(__file__), "..", "dep", "imgui", "include", "IconsFontAwesome5.h") fa_file = os.path.join(os.path.dirname(__file__), "..", "dep", "imgui", "include", "IconsFontAwesome5.h")
pf_file = os.path.join(os.path.dirname(__file__), "..", "dep", "imgui", "include", "IconsPromptFont.h") pf_file = os.path.join(os.path.dirname(__file__), "..", "dep", "imgui", "include", "IconsPromptFont.h")
dst_file = os.path.join(os.path.dirname(__file__), "..", "src", "util", "imgui_manager.cpp") emoji_file = os.path.join(os.path.dirname(__file__), "..", "dep", "imgui", "include", "IconsEmoji.h")
dst_file = os.path.join(os.path.dirname(__file__), "..", "src", "util", "imgui_glyph_ranges.inl")
all_source_files = glob.glob(os.path.join(src_dir, "**", "*.cpp"), recursive=True) + \ all_source_files = glob.glob(os.path.join(src_dir, "**", "*.cpp"), recursive=True) + \
glob.glob(os.path.join(src_dir, "**", "*.h"), recursive=True) + \ glob.glob(os.path.join(src_dir, "**", "*.h"), recursive=True) + \
@ -18,6 +19,7 @@ all_source_files = glob.glob(os.path.join(src_dir, "**", "*.cpp"), recursive=Tru
tokens = set() tokens = set()
pf_tokens = set() pf_tokens = set()
emoji_tokens = set()
for filename in all_source_files: for filename in all_source_files:
data = None data = None
with open(filename, "r") as f: with open(filename, "r") as f:
@ -28,8 +30,9 @@ for filename in all_source_files:
tokens = tokens.union(set(re.findall("(ICON_FA_[a-zA-Z0-9_]+)", data))) tokens = tokens.union(set(re.findall("(ICON_FA_[a-zA-Z0-9_]+)", data)))
pf_tokens = pf_tokens.union(set(re.findall("(ICON_PF_[a-zA-Z0-9_]+)", data))) pf_tokens = pf_tokens.union(set(re.findall("(ICON_PF_[a-zA-Z0-9_]+)", data)))
emoji_tokens = emoji_tokens.union(set(re.findall("(ICON_EMOJI_[a-zA-Z0-9_]+)", data)))
print("{}/{} tokens found.".format(len(tokens), len(pf_tokens))) print("{}/{}/{} tokens found.".format(len(tokens), len(pf_tokens), len(emoji_tokens)))
if len(tokens) == 0 and len(pf_tokens) == 0: if len(tokens) == 0 and len(pf_tokens) == 0:
sys.exit(0) sys.exit(0)
@ -46,9 +49,16 @@ with open(pf_file, "r") as f:
if match is None: if match is None:
continue continue
u8_encodings[match[1]] = bytes.fromhex(match[2].replace("\\x", "")) u8_encodings[match[1]] = bytes.fromhex(match[2].replace("\\x", ""))
with open(emoji_file, "r") as f:
for line in f.readlines():
match = re.match("#define (ICON_EMOJI_[^ ]+) \"([^\"]+)\"", line)
if match is None:
continue
u8_encodings[match[1]] = bytes.fromhex(match[2].replace("\\x", ""))
out_pattern = "(static constexpr ImWchar range_fa\[\] = \{)[0-9A-Z_a-z, \n]+(\};)" out_pattern = "(static constexpr ImWchar FA_ICON_RANGE\[\] = \{)[0-9A-Z_a-z, \n]+(\};)"
out_pf_pattern = "(static constexpr ImWchar range_pf\[\] = \{)[0-9A-Z_a-z, \n]+(\};)" out_pf_pattern = "(static constexpr ImWchar PF_ICON_RANGE\[\] = \{)[0-9A-Z_a-z, \n]+(\};)"
out_emoji_pattern = "(static constexpr ImWchar EMOJI_ICON_RANGE\[\] = \{)[0-9A-Z_a-z, \n]+(\};)"
def get_pairs(tokens): def get_pairs(tokens):
codepoints = list() codepoints = list()
@ -84,6 +94,7 @@ with open(dst_file, "r") as f:
original = f.read() original = f.read()
updated = re.sub(out_pattern, "\\1 " + get_pairs(tokens) + " \\2", original) updated = re.sub(out_pattern, "\\1 " + get_pairs(tokens) + " \\2", original)
updated = re.sub(out_pf_pattern, "\\1 " + get_pairs(pf_tokens) + " \\2", updated) updated = re.sub(out_pf_pattern, "\\1 " + get_pairs(pf_tokens) + " \\2", updated)
updated = re.sub(out_emoji_pattern, "\\1 " + get_pairs(emoji_tokens) + " \\2", updated)
if original != updated: if original != updated:
with open(dst_file, "w") as f: with open(dst_file, "w") as f:
f.write(updated) f.write(updated)

View File

@ -0,0 +1,8 @@
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
// SPDX-License-Identifier: (GPL-3.0 OR PolyForm-Strict-1.0.0)
static constexpr ImWchar FA_ICON_RANGE[] = { 0xe06f,0xe06f,0xe086,0xe086,0xf002,0xf002,0xf005,0xf005,0xf007,0xf007,0xf00c,0xf00e,0xf011,0xf011,0xf013,0xf013,0xf017,0xf017,0xf019,0xf019,0xf01c,0xf01c,0xf021,0xf021,0xf023,0xf023,0xf025,0xf025,0xf027,0xf028,0xf02e,0xf02e,0xf030,0xf030,0xf03a,0xf03a,0xf03d,0xf03d,0xf049,0xf04c,0xf050,0xf050,0xf05e,0xf05e,0xf062,0xf063,0xf067,0xf067,0xf071,0xf071,0xf075,0xf075,0xf077,0xf078,0xf07b,0xf07c,0xf084,0xf085,0xf091,0xf091,0xf0a0,0xf0a0,0xf0ac,0xf0ad,0xf0c5,0xf0c5,0xf0c7,0xf0c9,0xf0cb,0xf0cb,0xf0d0,0xf0d0,0xf0dc,0xf0dc,0xf0e2,0xf0e2,0xf0e7,0xf0e7,0xf0eb,0xf0eb,0xf0f1,0xf0f1,0xf0f3,0xf0f3,0xf0fe,0xf0fe,0xf110,0xf110,0xf119,0xf119,0xf11b,0xf11c,0xf140,0xf140,0xf14a,0xf14a,0xf15b,0xf15b,0xf15d,0xf15d,0xf191,0xf192,0xf1ab,0xf1ab,0xf1dd,0xf1de,0xf1e6,0xf1e6,0xf1eb,0xf1eb,0xf1f8,0xf1f8,0xf1fc,0xf1fc,0xf240,0xf240,0xf242,0xf242,0xf245,0xf245,0xf26c,0xf26c,0xf279,0xf279,0xf2d0,0xf2d0,0xf2db,0xf2db,0xf2f2,0xf2f2,0xf3c1,0xf3c1,0xf3fd,0xf3fd,0xf410,0xf410,0xf466,0xf466,0xf4ce,0xf4ce,0xf500,0xf500,0xf51f,0xf51f,0xf538,0xf538,0xf545,0xf545,0xf547,0xf548,0xf57a,0xf57a,0xf5a2,0xf5a2,0xf5aa,0xf5aa,0xf5e7,0xf5e7,0xf65d,0xf65e,0xf6a9,0xf6a9,0xf6cf,0xf6cf,0xf70c,0xf70c,0xf794,0xf794,0xf7a0,0xf7a0,0xf7c2,0xf7c2,0xf807,0xf807,0xf815,0xf815,0xf818,0xf818,0xf84c,0xf84c,0xf8cc,0xf8cc,0x0,0x0 };
static constexpr ImWchar PF_ICON_RANGE[] = { 0x2196,0x2199,0x219e,0x21a1,0x21b0,0x21b3,0x21ba,0x21c3,0x21c7,0x21ca,0x21d0,0x21d4,0x21dc,0x21dd,0x21e0,0x21e3,0x21ed,0x21ee,0x21f7,0x21f8,0x21fa,0x21fb,0x227a,0x227f,0x2284,0x2284,0x235e,0x235e,0x2360,0x2361,0x2364,0x2366,0x23b2,0x23b4,0x23ce,0x23ce,0x23f4,0x23f7,0x2427,0x243a,0x243c,0x243e,0x2460,0x246b,0x24f5,0x24fd,0x24ff,0x24ff,0x2717,0x2717,0x278a,0x278e,0x27fc,0x27fc,0xe001,0xe001,0xff21,0xff3a,0x1f52b,0x1f52b,0x0,0x0 };
static constexpr ImWchar EMOJI_ICON_RANGE[] = { 0x0,0x0 };

View File

@ -6,6 +6,7 @@
#include "host.h" #include "host.h"
#include "image.h" #include "image.h"
#include "imgui_fullscreen.h" #include "imgui_fullscreen.h"
#include "imgui_glyph_ranges.inl"
#include "input_manager.h" #include "input_manager.h"
#include "common/assert.h" #include "common/assert.h"
@ -19,6 +20,7 @@
#include "IconsFontAwesome5.h" #include "IconsFontAwesome5.h"
#include "fmt/format.h" #include "fmt/format.h"
#include "imgui.h" #include "imgui.h"
#include "imgui_freetype.h"
#include "imgui_internal.h" #include "imgui_internal.h"
#include <atomic> #include <atomic>
@ -64,6 +66,7 @@ static void UpdateScale();
static void SetStyle(); static void SetStyle();
static void SetKeyMap(); static void SetKeyMap();
static bool LoadFontData(); static bool LoadFontData();
static void ReloadFontDataIfActive();
static bool AddImGuiFonts(bool fullscreen_fonts); static bool AddImGuiFonts(bool fullscreen_fonts);
static ImFont* AddTextFont(float size); static ImFont* AddTextFont(float size);
static ImFont* AddFixedFont(float size); static ImFont* AddFixedFont(float size);
@ -80,6 +83,7 @@ static float s_global_scale = 1.0f;
static std::string s_font_path; static std::string s_font_path;
static std::vector<WCharType> s_font_range; static std::vector<WCharType> s_font_range;
static std::vector<WCharType> s_emoji_range;
static ImFont* s_standard_font; static ImFont* s_standard_font;
static ImFont* s_fixed_font; static ImFont* s_fixed_font;
@ -90,6 +94,7 @@ static DynamicHeapArray<u8> s_standard_font_data;
static DynamicHeapArray<u8> s_fixed_font_data; static DynamicHeapArray<u8> s_fixed_font_data;
static DynamicHeapArray<u8> s_icon_fa_font_data; static DynamicHeapArray<u8> s_icon_fa_font_data;
static DynamicHeapArray<u8> s_icon_pf_font_data; static DynamicHeapArray<u8> s_icon_pf_font_data;
static DynamicHeapArray<u8> s_emoji_font_data;
static float s_window_width; static float s_window_width;
static float s_window_height; static float s_window_height;
@ -122,22 +127,36 @@ void ImGuiManager::SetFontPathAndRange(std::string path, std::vector<WCharType>
s_font_path = std::move(path); s_font_path = std::move(path);
s_font_range = std::move(range); s_font_range = std::move(range);
s_standard_font_data = {}; s_standard_font_data = {};
ReloadFontDataIfActive();
}
if (ImGui::GetCurrentContext()) void ImGuiManager::SetEmojiFontRange(std::vector<WCharType> range)
{
static constexpr size_t builtin_size = std::size(EMOJI_ICON_RANGE);
const size_t runtime_size = range.size();
if (runtime_size == 0)
{ {
ImGui::EndFrame(); if (s_emoji_range.empty())
return;
if (!LoadFontData()) s_emoji_range = {};
Panic("Failed to load font data");
if (!AddImGuiFonts(HasFullscreenFonts()))
Panic("Failed to create ImGui font text");
if (!g_gpu_device->UpdateImGuiFontTexture())
Panic("Failed to recreate font texture after scale+resize");
NewFrame();
} }
else
{
if (!s_emoji_range.empty() && (s_emoji_range.size() - builtin_size) == range.size() &&
std::memcmp(s_emoji_range.data(), range.data(), range.size() * sizeof(ImWchar)) == 0)
{
// no change
return;
}
s_emoji_range = std::move(range);
s_emoji_range.resize(s_emoji_range.size() + builtin_size);
std::memcpy(&s_emoji_range[runtime_size], EMOJI_ICON_RANGE, sizeof(EMOJI_ICON_RANGE));
}
ReloadFontDataIfActive();
} }
void ImGuiManager::SetGlobalScale(float global_scale) void ImGuiManager::SetGlobalScale(float global_scale)
@ -518,6 +537,16 @@ bool ImGuiManager::LoadFontData()
s_icon_pf_font_data = std::move(font_data.value()); s_icon_pf_font_data = std::move(font_data.value());
} }
if (s_emoji_font_data.empty() && false)
{
std::optional<DynamicHeapArray<u8>> font_data =
Host::ReadCompressedResourceFile("fonts/TwitterColorEmoji-SVGinOT.ttf.zst", true);
if (!font_data.has_value())
return false;
s_emoji_font_data = std::move(font_data.value());
}
return true; return true;
} }
@ -539,28 +568,6 @@ ImFont* ImGuiManager::AddFixedFont(float size)
bool ImGuiManager::AddIconFonts(float size) bool ImGuiManager::AddIconFonts(float size)
{ {
static constexpr ImWchar range_fa[] = {
0xe06f, 0xe06f, 0xe086, 0xe086, 0xf002, 0xf002, 0xf005, 0xf005, 0xf007, 0xf007, 0xf00c, 0xf00e, 0xf011, 0xf011,
0xf013, 0xf013, 0xf017, 0xf017, 0xf019, 0xf019, 0xf01c, 0xf01c, 0xf021, 0xf021, 0xf023, 0xf023, 0xf025, 0xf025,
0xf027, 0xf028, 0xf02e, 0xf02e, 0xf030, 0xf030, 0xf03a, 0xf03a, 0xf03d, 0xf03d, 0xf049, 0xf04c, 0xf050, 0xf050,
0xf05e, 0xf05e, 0xf062, 0xf063, 0xf067, 0xf067, 0xf071, 0xf071, 0xf075, 0xf075, 0xf077, 0xf078, 0xf07b, 0xf07c,
0xf084, 0xf085, 0xf091, 0xf091, 0xf0a0, 0xf0a0, 0xf0ac, 0xf0ad, 0xf0c5, 0xf0c5, 0xf0c7, 0xf0c9, 0xf0cb, 0xf0cb,
0xf0d0, 0xf0d0, 0xf0dc, 0xf0dc, 0xf0e2, 0xf0e2, 0xf0e7, 0xf0e7, 0xf0eb, 0xf0eb, 0xf0f1, 0xf0f1, 0xf0f3, 0xf0f3,
0xf0fe, 0xf0fe, 0xf110, 0xf110, 0xf119, 0xf119, 0xf11b, 0xf11c, 0xf140, 0xf140, 0xf14a, 0xf14a, 0xf15b, 0xf15b,
0xf15d, 0xf15d, 0xf191, 0xf192, 0xf1ab, 0xf1ab, 0xf1dd, 0xf1de, 0xf1e6, 0xf1e6, 0xf1eb, 0xf1eb, 0xf1f8, 0xf1f8,
0xf1fc, 0xf1fc, 0xf240, 0xf240, 0xf242, 0xf242, 0xf245, 0xf245, 0xf26c, 0xf26c, 0xf279, 0xf279, 0xf2d0, 0xf2d0,
0xf2db, 0xf2db, 0xf2f2, 0xf2f2, 0xf3c1, 0xf3c1, 0xf3fd, 0xf3fd, 0xf410, 0xf410, 0xf466, 0xf466, 0xf4ce, 0xf4ce,
0xf500, 0xf500, 0xf51f, 0xf51f, 0xf538, 0xf538, 0xf545, 0xf545, 0xf547, 0xf548, 0xf57a, 0xf57a, 0xf5a2, 0xf5a2,
0xf5aa, 0xf5aa, 0xf5e7, 0xf5e7, 0xf65d, 0xf65e, 0xf6a9, 0xf6a9, 0xf6cf, 0xf6cf, 0xf70c, 0xf70c, 0xf794, 0xf794,
0xf7a0, 0xf7a0, 0xf7c2, 0xf7c2, 0xf807, 0xf807, 0xf815, 0xf815, 0xf818, 0xf818, 0xf84c, 0xf84c, 0xf8cc, 0xf8cc,
0x0, 0x0};
static constexpr ImWchar range_pf[] = {
0x2196, 0x2199, 0x219e, 0x21a1, 0x21b0, 0x21b3, 0x21ba, 0x21c3, 0x21c7, 0x21ca, 0x21d0, 0x21d4, 0x21dc,
0x21dd, 0x21e0, 0x21e3, 0x21ed, 0x21ee, 0x21f7, 0x21f8, 0x21fa, 0x21fb, 0x227a, 0x227f, 0x2284, 0x2284,
0x235e, 0x235e, 0x2360, 0x2361, 0x2364, 0x2366, 0x23b2, 0x23b4, 0x23ce, 0x23ce, 0x23f4, 0x23f7, 0x2427,
0x243a, 0x243c, 0x243e, 0x2460, 0x246b, 0x24f5, 0x24fd, 0x24ff, 0x24ff, 0x2717, 0x2717, 0x278a, 0x278e,
0x27fc, 0x27fc, 0xe001, 0xe001, 0xff21, 0xff3a, 0x1f52b, 0x1f52b, 0x0, 0x0};
{ {
ImFontConfig cfg; ImFontConfig cfg;
cfg.MergeMode = true; cfg.MergeMode = true;
@ -570,7 +577,8 @@ bool ImGuiManager::AddIconFonts(float size)
cfg.FontDataOwnedByAtlas = false; cfg.FontDataOwnedByAtlas = false;
if (!ImGui::GetIO().Fonts->AddFontFromMemoryTTF( if (!ImGui::GetIO().Fonts->AddFontFromMemoryTTF(
s_icon_fa_font_data.data(), static_cast<int>(s_icon_fa_font_data.size()), size * 0.75f, &cfg, range_fa)) s_icon_fa_font_data.data(), static_cast<int>(s_icon_fa_font_data.size()), size * 0.75f, &cfg, FA_ICON_RANGE))
[[unlikely]]
{ {
return false; return false;
} }
@ -585,7 +593,26 @@ bool ImGuiManager::AddIconFonts(float size)
cfg.FontDataOwnedByAtlas = false; cfg.FontDataOwnedByAtlas = false;
if (!ImGui::GetIO().Fonts->AddFontFromMemoryTTF( if (!ImGui::GetIO().Fonts->AddFontFromMemoryTTF(
s_icon_pf_font_data.data(), static_cast<int>(s_icon_pf_font_data.size()), size * 1.2f, &cfg, range_pf)) s_icon_pf_font_data.data(), static_cast<int>(s_icon_pf_font_data.size()), size * 1.2f, &cfg, PF_ICON_RANGE))
[[unlikely]]
{
return false;
}
}
if constexpr (false) // Not yet used
{
ImFontConfig cfg;
cfg.MergeMode = true;
cfg.PixelSnapH = true;
cfg.GlyphMinAdvanceX = size;
cfg.GlyphMaxAdvanceX = size;
cfg.FontDataOwnedByAtlas = false;
cfg.FontBuilderFlags = ImGuiFreeTypeBuilderFlags_LoadColor | ImGuiFreeTypeBuilderFlags_Bitmap;
if (!ImGui::GetIO().Fonts->AddFontFromMemoryTTF(
s_emoji_font_data.data(), static_cast<int>(s_emoji_font_data.size()), size * 0.9f, &cfg,
s_emoji_range.empty() ? EMOJI_ICON_RANGE : s_emoji_range.data())) [[unlikely]]
{ {
return false; return false;
} }
@ -632,6 +659,25 @@ bool ImGuiManager::AddImGuiFonts(bool fullscreen_fonts)
return io.Fonts->Build(); return io.Fonts->Build();
} }
void ImGuiManager::ReloadFontDataIfActive()
{
if (!ImGui::GetCurrentContext())
return;
ImGui::EndFrame();
if (!LoadFontData())
Panic("Failed to load font data");
if (!AddImGuiFonts(HasFullscreenFonts()))
Panic("Failed to create ImGui font text");
if (!g_gpu_device->UpdateImGuiFontTexture())
Panic("Failed to recreate font texture after scale+resize");
NewFrame();
}
bool ImGuiManager::AddFullscreenFontsIfMissing() bool ImGuiManager::AddFullscreenFontsIfMissing()
{ {
if (HasFullscreenFonts()) if (HasFullscreenFonts())

View File

@ -4,6 +4,7 @@
#pragma once #pragma once
#include "common/types.h" #include "common/types.h"
#include <span>
#include <string> #include <string>
#include <vector> #include <vector>
@ -21,6 +22,10 @@ using WCharType = u32;
/// Sets the path to the font to use. Empty string means to use the default. /// Sets the path to the font to use. Empty string means to use the default.
void SetFontPathAndRange(std::string path, std::vector<WCharType> range); void SetFontPathAndRange(std::string path, std::vector<WCharType> range);
/// Sets the emoji font range to use. Empty means no glyphs will be rasterized.
/// Should NOT be terminated with zeros, unlike the font range above.
void SetEmojiFontRange(std::vector<WCharType> range);
/// Changes the global scale. /// Changes the global scale.
void SetGlobalScale(float global_scale); void SetGlobalScale(float global_scale);