You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

关于Dear ImGui中Font/glyph合并与重映射的技术咨询

关于Dear ImGui中Font/glyph合并与重映射的技术咨询

嘿,刚好之前折腾过类似的需求,给你梳理下具体的实现思路和代码示例,应该能直接上手!

首先,你的需求完全可以通过ImFont::AddGlyph()来实现,核心就是从次要字体里提取目标字形,然后手动添加到主字体里,同时指定重映射的Unicode码点和位置偏移。

步骤拆解与代码实现

  • 第一步:加载主、次字体
    先把两个字体都加载进来,主字体作为最终要使用的默认字体,次字体只是用来提取字形,不需要设为默认:

    ImGuiIO& io = ImGui::GetIO();
    // 加载主字体,保留指针以便后续操作
    ImFont* primary_font = io.Fonts->AddFontFromFileTTF("path/to/your/primary.ttf", 16.0f);
    // 加载次字体,仅用于提取字形,不用设置为当前使用字体
    ImFont* secondary_font = io.Fonts->AddFontFromFileTTF("path/to/your/secondary.ttf", 16.0f);
    
  • 第二步:处理字形重映射与位置偏移
    这里我们可以用一个映射表来管理「原Unicode码点→目标码点」,再用另一个表存每个目标字形的偏移值,然后循环处理每个字形:

    // 定义字形重映射规则:key是次字体的原Unicode,value是要放到主字体的目标Unicode
    // 推荐用Unicode私人使用区(U+E000~U+F8FF),避免和主字体已有码点冲突
    std::unordered_map<ImWchar, ImWchar> glyph_remap = {
        {0x260E, 0xE000}, // 把次字体的电话符号重映射到主字体的0xE000
        {0x2764, 0xE001}  // 把爱心符号重映射到0xE001
    };
    
    // 定义每个目标字形的x/y偏移(根据你的需求调整数值)
    std::unordered_map<ImWchar, ImVec2> glyph_offsets = {
        {0xE000, ImVec2(2.0f, -1.0f)}, // 电话符号x偏移+2,y偏移-1
        {0xE001, ImVec2(1.0f, 0.0f)}   // 爱心符号x偏移+1,y不变
    };
    
    for (const auto& [src_unicode, dst_unicode] : glyph_remap) {
        // 从次字体中查找对应的字形数据
        const ImFontGlyph* src_glyph = secondary_font->FindGlyph(src_unicode);
        if (!src_glyph) {
            continue; // 找不到该字形就跳过
        }
    
        // 获取当前字形要应用的偏移
        const ImVec2& offset = glyph_offsets[dst_unicode];
    
        // 调用AddGlyph把字形添加到主字体,同时应用重映射和偏移
        primary_font->AddGlyph(
            secondary_font->Font,          // 传入次字体的底层FreeType对象
            dst_unicode,                   // 重映射后的目标Unicode码点
            src_glyph->X0 + offset.x,      // 调整后的字形左边界x
            src_glyph->Y0 + offset.y,      // 调整后的字形上边界y
            src_glyph->X1 + offset.x,      // 调整后的字形右边界x
            src_glyph->Y1 + offset.y,      // 调整后的字形下边界y
            src_glyph->U0,                 // 字形在纹理中的左UV坐标
            src_glyph->V0,                 // 字形在纹理中的上UV坐标
            src_glyph->U1,                 // 字形在纹理中的右UV坐标
            src_glyph->V1,                 // 字形在纹理中的下UV坐标
            src_glyph->AdvanceX            // 字形的水平步长(如果需要调整间距可以改这个值)
        );
    }
    
  • 第三步:更新字体纹理
    所有字形添加完成后,必须调用Build()让ImGui重新生成字体纹理,不然新添加的字形不会生效:

    io.Fonts->Build();
    

一些关键注意事项

  • 尽量用Unicode私人使用区作为重映射的目标码点,这样不会和主字体已有的标准码点冲突,避免显示异常。
  • 确保主、次字体的字号一致,不然字形大小不匹配,偏移调整会很麻烦;如果字号不同,你需要手动缩放X0/X1/Y0/Y1AdvanceX这些参数。
  • 如果需要批量处理多个字形,可以把原Unicode范围放进一个数组里循环,不用一个个写映射表。
  • 要是不确定某个参数的作用,可以去看ImGui源码里ImFont::AddGlyph()的注释,里面对每个参数的说明很详细。

要是还有疑问,多调几个参数试试效果,很快就能摸透啦!

备注:内容来源于stack exchange,提问作者ScratchingTheSurface

火山引擎 最新活动