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.
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; }