Direct2D-如何绘制尽可能接近GDI渲染的文本

时间:2018-11-19 20:48:20

标签: directwrite

我正在使用Embarcadero RAD Studio XE7编译器(C ++ Builder),在其中尝试提供的Direct2D API。我的目的是创建可以提供与GDI完全相同的呈现效果的应用程序,但也可以使Direct2D提供的新功能受益,例如Window 10上的彩色表情符号。

在这种情况下,我编写了一个小的文本绘制函数,该函数使用Direct2D来完成这项工作。我还搜索了如何配置Direct2D / DirectWrite以尽可能接近GDI的方式绘制文本。

这导致以下功能:

void DrawText_Direct2D(const std::wstring& text, const TRect& rect, TColor bgColor,
        TFont* pFont, TCanvas* pCanvas)
{
    if (!pFont || !pCanvas)
        return;

    // fill destination canvas background with provided color
    WGDIHelper::Fill(pCanvas, rect, bgColor);

    ::D2D1_RECT_F drawRect;
    drawRect.left   = rect.Left;
    drawRect.top    = rect.Top;
    drawRect.right  = rect.Right;
    drawRect.bottom = rect.Bottom;

    // get Direct2D destination canvas
    std::unique_ptr<TDirect2DCanvas> pD2DCanvas(new TDirect2DCanvas(pCanvas->Handle, rect));

    // configure Direct2D font
    pD2DCanvas->Font->Height      = pFont->Height;
    pD2DCanvas->Font->Name        = pFont->Name;
    pD2DCanvas->Font->Orientation = pFont->Orientation;
    pD2DCanvas->Font->Pitch       = pFont->Pitch;
    pD2DCanvas->Font->Style       = pFont->Style;

    // get DirectWrite text format object
    _di_IDWriteTextFormat pFormat = pD2DCanvas->Font->Handle;

    // found it?
    if (!pFormat)
        return;

    // get (or create) the DirectWrite factory
    _di_IDWriteFactory pDirectWrite = ::DWriteFactory(DWRITE_FACTORY_TYPE_SHARED);

    if (!pDirectWrite)
        return;

    // configure and apply new rendering parameters for DirectWrite
    _di_IDWriteRenderingParams renderParams;
    pDirectWrite->CreateCustomRenderingParams(1.0f, 0.0f, 0.0f, DWRITE_PIXEL_GEOMETRY_RGB,
            DWRITE_RENDERING_MODE_CLEARTYPE_GDI_NATURAL, &renderParams);
    pD2DCanvas->RenderTarget->SetTextRenderingParams(renderParams);

    // set antialiasing mode
    pD2DCanvas->RenderTarget->SetTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE);

    ::ID2D1SolidColorBrush* pBrush = NULL;

    WColor color(pFont->Color);

    // create solid color brush
    pD2DCanvas->RenderTarget->CreateSolidColorBrush(color.GetD2DColor(), &pBrush);

    if (!pBrush)
        return;

    // set horiz alignment
    pFormat->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_LEADING);

    // set vert alignment
    pFormat->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_NEAR);

    // set reading direction
    pFormat->SetReadingDirection(DWRITE_READING_DIRECTION_LEFT_TO_RIGHT);

    // set word wrapping mode
    pFormat->SetWordWrapping(DWRITE_WORD_WRAPPING_WRAP);

    IDWriteInlineObject* pInlineObject = NULL;

    ::DWRITE_TRIMMING trimming;
    trimming.delimiter      = 0;
    trimming.delimiterCount = 0;
    trimming.granularity    = DWRITE_TRIMMING_GRANULARITY_NONE;

    // set text trimming
    pFormat->SetTrimming(&trimming, pInlineObject);

    try
    {
        pD2DCanvas->BeginDraw();

        // draw the text
        pD2DCanvas->RenderTarget->DrawText(text.c_str(), text.length(), pFormat, drawRect, pBrush,
                D2D1_DRAW_TEXT_OPTIONS_NONE);
    }
    __finally
    {
        pD2DCanvas->EndDraw();
    }
}

上面的代码是我可以做得更接近GDI文本绘制的代码。但是,有些细微的差异我无法纠正。这是使用GDI和上述功能绘制的文本的屏幕截图(注意,下面的图像已拉伸,应以其实际大小打开):

enter image description here

如您所见,D2D文本比GDI文本长,并且在两个版本之间,几个单词(例如“ humanum”)明显不同。此外,在几个单词周围的抗锯齿有些不同,例如第一个“文本”单词(包括后面的逗号)会使这些单词再次显得有些模糊。

但是,对于我来说,具有相似但具有很小可见差异的图形是不可接受的选择。我需要一张没有差异的图纸,或者至少在差异几乎不可见的地方(即没有彻底的比较)。

使用相同的AFAIK字体,并且将正确的cleartype模式应用于DirectWrite。那么如何改善Direct2D渲染,使其看起来更接近旧的GDI?有办法吗?

注意,我已经找到并阅读了一些有关Direct2D和GDI之间的文本质量差异的话题,例如

Why can't DirectX/DirectWrite/Direct2D text rendering be as sharp as GDI?

Direct2D interface and blurry text issue

不幸的是,就我而言,我已经启用了建议的选项,以允许Direct2D绘制类似GDI的文本(至少我认为我做对了),但是结果仍然不够好,无法使用在我的项目中。

1 个答案:

答案 0 :(得分:0)

我建议改用CreateGdiCompatibleTextLayout,仅因为Direct2D中没有DrawTextInGdiCompatibleWay()函数变体。它不一定与gdi输出完全匹配,但希望会更接近。