Page's Personal Website

Imgui最后字符乱码问题

2020-09-21
c++

[TOC]

用Imgui显示中文的时候,最后一个字符偶尔乱码~

描述

  • 打印了一下十六进制
int ImFormatStringV(char* buf, size_t buf_size, const char* fmt, va_list args)
{
#ifdef IMGUI_USE_STB_SPRINTF
    int w = stbsp_vsnprintf(buf, (int)buf_size, fmt, args);
#else
    int w = vsnprintf(buf, buf_size, fmt, args);
#endif
    if (buf == NULL)
        return w;
    // test
    if (w == -1)
    {
        for (int i = 0; i < strlen(fmt); ++i)
        {
            printf("%2X ", fmt[i]);
        }
        printf("err(%d) %d\n", errno, buf_size);
    }
    // test end
    if (w == -1 || w >= (int)buf_size)
        w = (int)buf_size - 1;

    buf[w] = 0;
    return w;
}
#endif // #ifdef IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS

打印结果,找到一个查编码的网页[4],不错

FFFFFFE6 FFFFFF97 FFFFFFB6 FFFFFFE9 FFFFFF97 FFFFFFB4 FFFFFFE9 FFFFFF80 FFFFFF9F FFFFFFE5 FFFFFFBA FFFFFFA6 FFFFFFEF FFFFFFBC FFFFFF9A err(22) 3073
时:xE6\x97\xB6
间:xE9\x97\xB4
速:\xE9\x80\x9F
度:\xE5\xBA\xA6
中文冒号:\xEF\xBC\x9A

原因

  • 看到错误码是22 == EINVAL,就知道原因了。因为调用到vsnprintf的时候,格式化后面没有参数,如下所示:
ImGui.Text("时间速度:");

这就导致ImFormatStringV函数返回了整个buf_size,而Imgui把这整个size都显示到界面上,就GG了

errno

参考[2][3],还有errno.h

// Error codes used in the Secure CRT functions
#ifndef RC_INVOKED
    #define _SECURECRT_ERRCODE_VALUES_DEFINED
    #define EINVAL          22
    #define ERANGE          34
    #define EILSEQ          42
    #define STRUNCATE       80
#endif

解决

  • 方法1:修改ImFormatStringV中vsnprintf返回-1,主要是(errno==EINVAL)的情况,再取源字符串的长度判断一下
    if (w == -1 || w >= (int)buf_size)
    {
        w = strlen(fmt);
        w = w < buf_size - 1 ? w : (int)buf_size - 1;
    }

看ImGui::TextEx里面其实也做了一层判断,只是再ImFormatStringV的时候走不到而已

void ImGui::TextEx(const char* text, const char* text_end, ImGuiTextFlags flags)
{
    ImGuiWindow* window = GetCurrentWindow();
    if (window->SkipItems)
        return;

    ImGuiContext& g = *GImGui;
    IM_ASSERT(text != NULL);
    const char* text_begin = text;
    if (text_end == NULL)
        text_end = text + strlen(text); // FIXME-OPT
  • 方法2:把buf清0

理论上把g.TempBuffer格式化就可以了,没测试过,不知道有木有影响~

void ImGui::TextV(const char* fmt, va_list args)
{
    ImGuiWindow* window = GetCurrentWindow();
    if (window->SkipItems)
        return;

    ImGuiContext& g = *GImGui;
    const char* text_end = g.TempBuffer + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args);
    TextEx(g.TempBuffer, text_end, ImGuiTextFlags_NoWidthForLargeClippedText);
}
  • 方法3:使用Imgui.TextUnformatted显示没有参数的文本

参考

[1]vsnprintf、_vsnprintf、_vsnprintf_l、_vsnwprintf、_vsnwprintf_l

[2]vsnprintf

[3]errno

[4]在线编码插叙


Similar Posts

Comments