【问题标题】:How to initialize Directwrite IDWriteTextLayout properly in a Direct2D window如何在 Direct2D 窗口中正确初始化 Directwrite IDWriteTextLayout
【发布时间】:2026-01-16 05:50:02
【问题描述】:

我正在尝试使用 Directwrite 将文本写入我的ID2D1HwndRenderTarget* renderTarget-window。效果很好,文本出现在它应该出现的地方。但我感觉我在Graphics::DrawMyText 做错了什么。我想我也应该在Graphics::Initialisation 中创建我的IDWriteTextLayout* textLayout,但如果我这样做,我将无法再更改文本const wchar_t* wszText。至少,我在IDWriteTextLayout接口中没有找到任何辅助函数

那么一直创建和释放我的IDWriteTextLayout 是否正确,或者有没有一种方法我只需要像其他接口一样创建一次?

#include<dwrite.h>

class Graphics
{
    IDWriteFactory* writeFactory;
    IDWriteTextLayout* textLayout;
    IDWriteTextFormat* textFormat; 
}

Graphics() // constructor
{
    writeFactory = NULL;
    textLayout = NULL;
    textFormat = NULL; 
}

Graphics::~Graphics() // destructor
{
    if (writeFactory) writeFactory->Release();
    if (textLayout) textLayout->Release();
    if (textFormat) textFormat->Release();
}

bool Graphics::Initialise(HWND windowsHandle)
{
    res = writeFactory->CreateTextFormat(
    L"Lucida Console",
    NULL,
    DWRITE_FONT_WEIGHT_REGULAR,
    DWRITE_FONT_STYLE_NORMAL,
    DWRITE_FONT_STRETCH_NORMAL,
    10.0f,
    L"en-us",
    &textFormat
    );
    if (res != S_OK) return false;

    return true;
}

void Graphics::DrawMyText(const wchar_t* wszText, float x, float y, float boxWidth,
                          float boxHeight, float r, float g, float b, float a)
{
    writeFactory->CreateTextLayout(wszText, (UINT32)wcslen(wszText), textFormat,
                                   boxWidth, boxHeight, &textLayout);
    brush->SetColor(D2D1::ColorF(r, g, b, a));
    renderTarget->DrawTextLayout(D2D1::Point2F(x, y), textLayout, brush);
    textLayout->Release(); // don't forget this one to prevent memory leaks
}

【问题讨论】:

  • renderTarget 是从哪里来的?
  • 如果您更改文本,您将不得不重新创建布局,但您可以保留格式,这就是您所做的。您还可以缓存布局,直到 CreateTextLayout 的参数发生更改。 docs.microsoft.com/en-us/windows/win32/directwrite/…
  • @MariusBancila 渲染目标已在其他地方初始化和定义,但这与这个问题无关,但我列出了它的名称和类型。
  • CreateTextLayout 接收一个文本字符串并生成一个表示完全分析和格式化结果的对象。如果您想更改文本字符串内容,您可以使用新的文本字符串再次调用CreateTextLayout
  • @RitaHan-MSFT 感谢您的建议。这就是我在显示的代码中所做的,但我希望我可以创建一次 IDWriteTextLayout 并像其他 ID-elements 一样重用它。

标签: c++ winapi directwrite


【解决方案1】:

DWrite 布局的文本确实是固定的,您可以选择创建(和释放)布局对象,或者采用更难的方式使用 IDWriteTextAnalyzer(以及 IDWriteTextAnalysisSource/IDWriteTextAnalysisSink)。如果布局的文本是可变的,那就太好了,但 MS 根本没有做出这样的选择。

【讨论】:

  • 是的,本来可以很容易地添加一个IDWriteTextLayout::SetText 和相应的IDWriteTextLayout::InsertRange/DeleteRange 函数(事后看来我们应该有),但那时我们基本上有两种客户(a)想要重复绘制大部分静态文本的简单应用程序 (b) 拥有自己的文本后备存储并有足够的愿望直接调用低级文本分析 API 的低级客户。
  • 我实际上发现 uniscribe 比 DWrite 更容易处理。主要是因为 uniscribe 仍然可以遵守 GDI SetTextAlignment 设置,因此您可以使用基线对齐并让渲染引擎处理文本显示为好像它在单行上一样。 (IDWriteTextLayout 会这样做,但使用 IDWriteTextFormat 会很痛苦)。