如何加快打印 Unicode?

Posted

技术标签:

【中文标题】如何加快打印 Unicode?【英文标题】:How To Speed Up Printing Unicode? 【发布时间】:2021-07-26 23:09:54 【问题描述】:

我正在使用 windows.h "TextOutW" 函数将文本打印到位图。打印常规 ascii 确实很快,但 unicode 范围内的打印似乎会导致速度大大减慢。这是我打印 unicode 方块并测试持续时间的代码:

wchar_t b = 0x25A0;
LPCWSTR s = &b;
TextOutW(hMyDC, x, y, s, wcslen(s));

有什么方法可以加快速度吗?

编辑: 我用计时器运行我的循环来测试速度:

    using std::chrono::high_resolution_clock;
    using std::chrono::duration_cast;
    using std::chrono::duration;
    using std::chrono::milliseconds;



auto t1 = high_resolution_clock::now();

for(int i = 0; i<1000; i++)
    TextOutW(hMyDC, 25, 25, s, 1);

  
auto t2 = high_resolution_clock::now();

  //  auto ms_int = duration_cast<milliseconds>(t2 - t1);
duration<double, std::milli> ms_double = t2 - t1;
    cout << ms_double.count() << "ms\n";

打印 'H' 1000 次大约需要 5 毫秒 打印 0x25A0 1000 次大约需要 50 毫秒

【问题讨论】:

您没有将有效字符串传递给 wcslenTextOutW 不,不会。您正在使用 Unicode API 和显式宽字符。这些都不取决于构建配置。如果您切换到TCHARs,情况就会发生变化,正如我所指出的,没有人再使用它了。这是 Microsoft 有 2 种不同的操作系统系列时的遗留物,一种不支持 Unicode,另一种支持。今天,只有一个操作系统。无论如何,我们仍然看不到您是如何确定减速的。 @i486 Windows 本身在内部使用宽字符,因此如果您使用 ANSI 模式函数,它无论如何都必须进行转换。您之前的示例不能用作 ANSI 字符串,因为 0x25a0 不适合单个字节,即使可以,也不是有效字符。 @i48 通用文本映射(如TCHAR)的唯一目的是能够编译单个源代码库以在 Windows NT 和 Windows 9x 上运行. MSLU 甚至缓解了这种需求。 MSLU 已在 两年 年前发货。因此,即使您是少数仍然需要针对已失去支持超过 15 年的操作系统的开发人员之一,您甚至不需要TCHARs。虽然,你是对的,当然。我应该说:“没有人会推荐使用TCHARs” @i48 LPCWSTR 的用途与TCHAR 完全不同。它将语言级别的类型映射到 ABI 类型。与TCHAR 不同,LPCWSTR 是明确的。如果您在代码中看到它,毫无疑问它映射到什么。与TCHAR 不同,它需要您的代码阅读者查看您的构建配置以了解它是什么,并提出更改该代码是否需要与 ANSI 和 Unicode 兼容的问题。使用TCHAR 会使代码更难阅读和理解,完全没有任何好处。 【参考方案1】:

问题在于您指向的是单个字符,而不是字符串。 wcslen 表现出未定义的行为并且可能返回一个非常大的数字。将其替换为1,速度应该会大大加快。

【讨论】:

像这样:TextOutW(hMyDC, x, y, s, 1); ? @NO_GUI 是的,完全正确。 它确实加快了一点,但它似乎仍然比绘制 H 慢得多 我们既看不到进行渲染的minimal reproducible example,也看不到您测量速度的方式。任何一个都可能是错的。 @IInspectable Ooo 好的,我会添加更多【参考方案2】:

您正在点击 Uniscribe。 ExtTextOutWTextOutW 检查文本是否应该通过 Uniscribe 串接或直接传递给 GDI。

为了避免 Uniscribe 开销(不推荐),您可以将 ETO_IGNORELANGUAGE 传递给 ExtTextOutW,但您会错过一些更高级的脚本(CJK aka Chinese+Japan+Korean,从右到左书写,字符根据位置改变形状它们被放置等)或根本没有文字。

对于 0x25A0 字符,我会减速大约 8 倍

对于 0x6F22 减速增加至 17×

在我的系统上,当指定ETO_IGNORELANGUAGE 时,没有减速。 0x25A0显示正确,0x6F22被默认框替换。

【讨论】:

感谢您的回答!我无法在我的系统上获得指定 ETO_IGNORELANGUAGE 的方形字符,但我使用了 ETO_OPAQUE 选项来获得类似的效果。 :ExtTextOutW(hMyDC, x, y, ETO_OPAQUE | ETO_IGNORELANGUAGE, NULL, s, 1, NULL);【参考方案3】:

这不是答案。我只是想发布代码来确认这个问题的时间。

注释掉的字符需要 7-8 毫秒,0x25A0 需要大约 50 毫秒。

我使用Times New Roman,因为this page 声称它有那个UNICODE 符号。

#include <iostream>
#include <chrono>
#include <windows.h>

int main()

    wchar_t b = 0x25A0;
    //wchar_t b = 0x0416;
    //wchar_t b = L'H';

    LPCWSTR s = &b;
    HDC hDC = ::GetWindowDC(::GetDesktopWindow());
    HFONT hFont = CreateFontW(36, 20, 0, 0, FW_DONTCARE, FALSE, TRUE, FALSE, DEFAULT_CHARSET, OUT_OUTLINE_PRECIS,
        CLIP_DEFAULT_PRECIS, CLEARTYPE_QUALITY, VARIABLE_PITCH, L"Times New Roman");
    HGDIOBJ hOld = ::SelectObject(hDC, hFont);
    auto t1 = std::chrono::steady_clock::now();
    for (int i = 0; i < 1000; i++) 
        TextOutW(hDC, 25, 25, s, 1);
    
    auto t2 = std::chrono::steady_clock::now();

    std::cout << std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1).count() << " msec" << std::endl;
    return 0;

【讨论】:

以上是关于如何加快打印 Unicode?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Swift 中打印 Unicode 字符

如何替换 Java 中不可打印的 Unicode 字符?

用java如何把unicode码转成汉字?

在 Java 中打印 Unicode 或补充字符

使用 bs4 和请求处理后如何正确打印出 unicode 文本? [复制]

在 Eclipse Pydev 控制台和 Idle 中打印 Unicode