Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Direct2D - How to draw a text as close as possible to GDI rendering

Tags:

directwrite

I'm working with Embarcadero RAD Studio XE7 compiler (C++ Builder), in which I'm trying the provided Direct2D API. My purpose is to create applications which provide the exact same rendering as the GDI did, but which also may benefit the new features Direct2D provides, like e.g the colored emojis on Window 10.

In this context, I wrote a small text drawing function, which uses Direct2D to do the job. I also searched about how to configure Direct2D/DirectWrite to draw a text as close as possible to what GDI does.

This resulted to the following function:

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();
    }
}

The code above is what I can do closer to the GDI text drawing. There are, however, small differences that I can not correct. Here is a screenshot of a text drawn with the GDI, and with the above function (NOTE that the image below is stretched and should be opened in his real size):

enter image description here

As you can see, the D2D text is longer than the GDI one, and several words, like "humanum", are clearly drawn differently between the both versions. Furthermore the antialiasing is somewhat different around several words, like the first "text" word, including the following comma, which cause these words to appear somewhat blurry again.

However, having a drawing similar but with small visible differences isn't an acceptable option for me. I need a drawing without difference, or at least where the differences are almost not visible (i.e without a thorough comparison).

AFAIK the same font are used, and the correct cleartype mode was applied to DirectWrite. So how can I improve my Direct2D rendering to look even closer to the good old GDI? Is there a way to do that?

NOTE I already found and read several threads speaking about the difference in text quality between Direct2D and GDI, like e.g

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

Direct2D interface and blurry text issue

Unfortunately, in my case, I already enabled the proposed options to allow Direct2D to draw the text like the GDI (at least I think I did that correctly), but the result is still not good enough to allow me to use it in my projects.

like image 377
Jean-Milost Reymond Avatar asked Oct 16 '22 10:10

Jean-Milost Reymond


1 Answers

I suggest using CreateGdiCompatibleTextLayout instead, simply because there is no DrawTextInGdiCompatibleWay function variant in Direct2D. It won't necessarily match gdi output exactly, but hopefully will be closer.

like image 76
bunglehead Avatar answered Oct 21 '22 06:10

bunglehead