Direct2D/DirectWrite Text Resolution

Started by
0 comments, last by Rockaway 7 years, 8 months ago

Hey,

I've just been playing about with Direct2D this afternoon, and I've moved on to using DIrectWrite for outputting my text.

All easy enough, but I can't for the life of me figure out how to increase the resolution of the text. Anything I draw to the screen with Direct2D scales nicely, but text doesn't.

I have two screens, one at 1680x1050, the other at 3840x2160 and it is quite noticeable on the higher resolution screen.

[attachment=33027:D2D_Resolution.jpg]

You can see the difference between the window title, and the sample text i've done with DirectWrite.

Anyone shed any light on this?

The code follows, i've removed the extraneous extra code to focus on the DirectWrite stuff.

Thanks.


#pragma once

#include <d2d1.h>
#include <dwrite.h>
#include <string.h>
#include <Windows.h>

    template<class Interface>
    inline void SafeRelease( Interface **ppInterfaceToRelease )
    {
        if ( *ppInterfaceToRelease != NULL )
        {
            ( *ppInterfaceToRelease )->Release( );

            ( *ppInterfaceToRelease ) = NULL;
        }
    }

class Application
{
    // ================================================================
    // --> FIELD
    // ================================================================

        private : FLOAT                  m_dpiX;
        private : FLOAT                  m_dpiY;
        private : HWND                   m_hWnd;
        private : ID2D1Factory*          m_pDirect2dFactory;
        private : ID2D1HwndRenderTarget* m_pRenderTarget;
        private : ID2D1SolidColorBrush*  m_pLightSlateGrayBrush;
        private : IDWriteFactory*        m_pDWriteFactory;
        private : IDWriteTextFormat*     m_pTextFormat;
        private : IDWriteTextLayout*     m_pTextLayout;
                  

    // ================================================================
    // --> CONSTRUCTOR
    // ================================================================

        public : Application( );

    // ================================================================
    // --> METHOD
    // ================================================================
 
        // METHOD -> CreateDeviceIndependentResources()
        private : HRESULT CreateDeviceIndependentResources( );

        // METHOD -> CreateDeviceDependentResources()
        private : HRESULT CreateDeviceDependentResources( );

        // METHOD -> DiscardDeviceDependentResources()
        private : void DiscardDeviceDependentResources( );

        // METHOD -> Initialise()
        public : HRESULT Initialise( HINSTANCE hInstance );

        // METHOD -> MessageLoop()
        public : void MessageLoop( );

        // METHOD -> OnRender()
        private : HRESULT OnRender( );

        // METHOD -> OnRender()
        private: void OnResize( UINT height, UINT width );

        // METHOD -> Procedure()
        private : static LRESULT CALLBACK Procedure( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam );

    // ================================================================
    // --> DESTRUCTOR
    // ================================================================

        public : ~Application( );
};

#include "Application.h"

// ================================================================
// CONSTRUCTOR
// ================================================================

    // PUBLIC
    Application::Application( ) : m_hWnd( NULL ),
                                  m_pDirect2dFactory( NULL ),
                                  m_pRenderTarget( NULL ),
                                  m_pLightSlateGrayBrush( NULL ),
                                  m_pDWriteFactory( NULL ),
                                  m_pTextFormat( NULL )
    {

    }

// ================================================================
// METHOD -> CreateDeviceIndependentResources()
// ================================================================

    // PRIVATE
    HRESULT Application::CreateDeviceIndependentResources( )
    {
        HRESULT hr = S_OK;

        hr = D2D1CreateFactory( D2D1_FACTORY_TYPE_SINGLE_THREADED, &m_pDirect2dFactory );

        if ( SUCCEEDED( hr ) )
        {
            hr = DWriteCreateFactory( DWRITE_FACTORY_TYPE_SHARED,
                                      __uuidof( IDWriteFactory ),
                                      reinterpret_cast<IUnknown**>( &m_pDWriteFactory ) );
        }

        if ( SUCCEEDED( hr ) )
        {
            hr = m_pDWriteFactory->CreateTextFormat(
                L"Segoe UI",                // Font family name.
                NULL,                       // Font collection (NULL sets it to use the system font collection).
                DWRITE_FONT_WEIGHT_REGULAR,
                DWRITE_FONT_STYLE_NORMAL,
                DWRITE_FONT_STRETCH_NORMAL,
                12.0f,
                L"en-us",
                &m_pTextFormat
            );  
        }

        return hr;
    }

// ================================================================
// METHOD -> CreateDeviceDependentResources()
// ================================================================

    // PRIVATE
    HRESULT Application::CreateDeviceDependentResources()
    {
        HRESULT hr = S_OK;

        if ( !m_pRenderTarget )
        {
            RECT rc;

            GetClientRect( m_hWnd, &rc );

            D2D1_SIZE_U size = D2D1::SizeU( rc.right - rc.left, rc.bottom - rc.top );

            // Create a Direct2D render target.
            hr = m_pDirect2dFactory->CreateHwndRenderTarget( D2D1::RenderTargetProperties( ),
                                                             D2D1::HwndRenderTargetProperties( m_hWnd, size ),
                                                             &m_pRenderTarget );
           

            if ( SUCCEEDED( hr ) )
            {
                // Create a black brush.
                hr = m_pRenderTarget->CreateSolidColorBrush( D2D1::ColorF( D2D1::ColorF( 0.0F, 0.0F, 0.0F, 1.0F ) ),
                    &m_pLightSlateGrayBrush );
            }
        }

        return hr;
    }

// ================================================================
// METHOD -> DiscardDeviceDependentResources()
// ================================================================

    // PRIVATE
    void Application::DiscardDeviceDependentResources( )
    {
        SafeRelease( &m_pRenderTarget );
        SafeRelease( &m_pLightSlateGrayBrush );
        SafeRelease( &m_pDWriteFactory );
        SafeRelease( &m_pTextFormat );
        SafeRelease( &m_pTextLayout );
    }

// ================================================================
// METHOD -> Initialise()
// ================================================================

    // PUBLIC
    HRESULT Application::Initialise( HINSTANCE hInstance )
    {
        HRESULT hr;

        hr = CreateDeviceIndependentResources( );

        if ( SUCCEEDED( hr ) )
        {
            // Register the window class.
            WNDCLASSEX wcex = { sizeof( WNDCLASSEX ) };

            wcex.style = CS_HREDRAW | CS_VREDRAW;
            wcex.lpfnWndProc = Application::Procedure;
            wcex.cbClsExtra = 0;
            wcex.cbWndExtra = sizeof( LONG_PTR );
            wcex.hInstance = hInstance;
            wcex.hbrBackground = NULL;
            wcex.lpszMenuName = NULL;
            wcex.hCursor = LoadCursor( NULL, IDI_APPLICATION );
            wcex.lpszClassName = L"DirectX 12 Application";

            RegisterClassEx( &wcex );

            m_pDirect2dFactory->GetDesktopDpi( &m_dpiX, &m_dpiY );

            // Create the window.
            m_hWnd = CreateWindow( L"DirectX 12 Application",
                L"DirectX 12 Application",
                WS_OVERLAPPEDWINDOW,
                CW_USEDEFAULT,
                CW_USEDEFAULT,
                1024,
                768,
                NULL,
                NULL,
                hInstance,
                this
            );

            hr = m_hWnd ? S_OK : E_FAIL;

            if ( SUCCEEDED( hr ) )
            {
                ShowWindow( m_hWnd, SW_SHOWNORMAL );
                UpdateWindow( m_hWnd );
            }
        }

        return hr;
    }

// ================================================================
// // METHOD -> MessageLoop()
// ================================================================

    // PUBLIC
    void Application::MessageLoop( )
    {
        MSG msg;

        while ( GetMessage( &msg, NULL, 0, 0 ) )
        {
            TranslateMessage( &msg );
            DispatchMessage( &msg );
        }
    }

// ================================================================
// METHOD -> OnRender()
// ================================================================

    // PRIVATE
    HRESULT Application::OnRender( )
    {
        HRESULT hr = S_OK;

        hr = CreateDeviceDependentResources( );

        if ( SUCCEEDED( hr ) )
        {
            m_pRenderTarget->BeginDraw( );

            m_pRenderTarget->SetTransform( D2D1::Matrix3x2F::Identity( ) );

            m_pRenderTarget->Clear( D2D1::ColorF( D2D1::ColorF::White ) );

            D2D1_SIZE_F rtSize = m_pRenderTarget->GetSize( );

            // START : DIRECTWRITE

            // Create a text layout using the text format.
            if ( SUCCEEDED( hr ) )
            {


                D2D1_SIZE_F rtSize = m_pRenderTarget->GetSize( );

                // Align text to centre of layout box.
                m_pTextFormat->SetTextAlignment( DWRITE_TEXT_ALIGNMENT_CENTER );
                m_pTextFormat->SetParagraphAlignment( DWRITE_PARAGRAPH_ALIGNMENT_CENTER );

                const wchar_t* wszText_ = L"DirectX 12 Application";

                UINT32 cTextLength_ = ( UINT32 )wcslen( wszText_ );

                hr = m_pDWriteFactory->CreateTextLayout(
                    wszText_,       // The string to be laid out and formatted.
                    cTextLength_,   // The length of the string.
                    m_pTextFormat,  // The text format to apply to the string (contains font information, etc).
                    300.0F,         // The width of the layout box.
                    100.0F,         // The height of the layout box.
                    &m_pTextLayout  // The IDWriteTextLayout interface pointer.
                );
            }

            m_pRenderTarget->DrawTextLayout(
                D2D1_POINT_2F { ( ( rtSize.width / 2 ) - 150.0F ), ( ( rtSize.height / 2 ) - 50.0F ) },
                m_pTextLayout,
                m_pLightSlateGrayBrush,
                D2D1_DRAW_TEXT_OPTIONS_NONE
            );

            // END : DIRECTWRITE
            // ----------------------------------------------------------------

            hr = m_pRenderTarget->EndDraw( );
        }

        if ( hr == D2DERR_RECREATE_TARGET )
        {
            hr = S_OK;
            DiscardDeviceDependentResources( );
        }

        return hr;
    }

// ================================================================
// METHOD -> OnResize()
// ================================================================

    // PRIVATE
    void Application::OnResize( UINT height, UINT width )
    {
        if ( m_pRenderTarget )
        {
            m_pRenderTarget->Resize( D2D1::SizeU( width, height ) );
        }
    }

// ================================================================
// METHOD -> Procedure()
// ================================================================

    // PRIVATE, STATIC
    LRESULT CALLBACK Application::Procedure( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
    {
        LRESULT result = 0;

        if ( uMsg == WM_CREATE )
        {
            LPCREATESTRUCT pcs = ( LPCREATESTRUCT )lParam;

            Application *pApplication = ( Application * )pcs->lpCreateParams;

            ::SetWindowLongPtrW( hWnd,
                GWLP_USERDATA,
                PtrToUlong( pApplication )
            );

            result = 1;
        }
        else
        {
            Application *pApplication = reinterpret_cast< Application * >( static_cast< LONG_PTR >( ::GetWindowLongPtrW( hWnd, GWLP_USERDATA ) ) );

            bool wasHandled = false;

            if ( pApplication )
            {
                switch ( uMsg )
                {
                    case WM_DESTROY :
                        {
                            PostQuitMessage( 0 );
                        }

                        result = 1;

                        wasHandled = true;

                        break;

                    case WM_DISPLAYCHANGE :
                        {
                            InvalidateRect( hWnd, NULL, FALSE );
                        }

                        result = 0;

                        wasHandled = true;

                        break;

                    case WM_PAINT :
                        {
                            pApplication->OnRender( );

                            ValidateRect( hWnd, NULL );
                        }

                        result = 0;

                        wasHandled = true;

                        break;

                    case WM_SIZE :
                        {
                            UINT height = HIWORD( lParam );
                            UINT width = LOWORD( lParam );
                            
                            pApplication->OnResize( height, width );
                        }

                        result = 0;

                        wasHandled = true;

                        break;
                }
            }

            if ( !wasHandled )
            {
                result = DefWindowProc( hWnd, uMsg, wParam, lParam );
            }
        }

        return result;
    }

    // ================================================================
    // DESTRUCTOR
    // ================================================================

    // PUBLIC
    Application::~Application( )
    {
        SafeRelease( &m_pDirect2dFactory );
        SafeRelease( &m_pRenderTarget );
        SafeRelease( &m_pLightSlateGrayBrush );
        SafeRelease( &m_pDWriteFactory );
        SafeRelease( &m_pTextFormat );
        SafeRelease( &m_pTextLayout );
    }

#include "Application.h"

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int CmdShow )
{
    HeapSetInformation( NULL, HeapEnableTerminationOnCorruption, NULL, 0 );

    if ( SUCCEEDED( CoInitialize( NULL ) ) )
    {
        Application App;

        if ( SUCCEEDED( App.Initialise( hInstance ) ) )
        {
            App.MessageLoop( );
        }

        CoUninitialize( );
    }

    return 0;
}
Advertisement

Fixed.

You have to declare that the application is DPI-Aware, by including the following manifest in your project settings.

[attachment=33036:D2D_Resolution_Fixed.jpg]

DeclareDPIAware.manifest


<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3" >
  <asmv3:application>
    <asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
      <dpiAware>true</dpiAware>
    </asmv3:windowsSettings>
  </asmv3:application>
</assembly>

Obviously, this means you are responsible for scaling, use ID2D1Factory::GetDesktopDpi() to get desktop DPI.

This topic is closed to new replies.

Advertisement