60

Imgui最后字符乱码问题

 3 years ago
source link: http://pkxpp.github.io/2020/09/21/Imgui最后字符乱码问题/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

[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] 在线编码插叙


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK