Created
February 11, 2022 22:18
-
-
Save fourst4r/1a16587608bdacd6aad22cf170426269 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module multiformattedtext; | |
/************************************************************************ | |
* | |
* File: MultiformattedText.cpp | |
* | |
* Description: | |
* | |
* | |
* This file is part of the Microsoft Windows SDK Code Samples. | |
* | |
* Copyright (C) Microsoft Corporation. All rights reserved. | |
* | |
* This source code is intended only as a supplement to Microsoft | |
* Development Tools and/or on-line documentation. See these other | |
* materials for detailed information regarding Microsoft code samples. | |
* | |
* THIS CODE AND INFORMATION ARE PROVIDED AS IS WITHOUT WARRANTY OF ANY | |
* KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |
* IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A | |
* PARTICULAR PURPOSE. | |
* | |
************************************************************************/ | |
pragma(lib, "gdi32.lib"); | |
pragma(lib, "comdlg32.lib"); | |
pragma(lib, "winmm.lib"); | |
pragma(lib, "ole32.lib"); | |
pragma(lib, "dwrite.lib"); | |
pragma(lib, "d2d1.lib"); | |
import directx.dwrite; | |
import directx.d2d1; | |
import directx.d2d1helper; | |
void SafeRelease(T : IUnknown)(ref T ppInterfaceToRelease) | |
{ | |
if (ppInterfaceToRelease !is null) | |
{ | |
ppInterfaceToRelease.Release(); | |
ppInterfaceToRelease = null; | |
} | |
} | |
auto toUTF16z(S)(S s) | |
{ | |
import std.utf : count, toUTFz; | |
return toUTFz!(const(wchar)*)(s); | |
} | |
class MultiformattedText | |
{ | |
private: | |
HWND hwnd_; | |
// How much to scale a design that assumes 96-DPI pixels. | |
float dpiScaleX_; | |
float dpiScaleY_; | |
// Direct2D | |
ID2D1Factory pD2DFactory_; | |
ID2D1HwndRenderTarget pRT_; | |
ID2D1SolidColorBrush pBlackBrush_; | |
// DirectWrite | |
IDWriteFactory pDWriteFactory_; | |
IDWriteTextFormat pTextFormat_; | |
IDWriteTextLayout pTextLayout_; | |
const(wchar)* wszText_; | |
UINT32 cTextLength_; | |
public: | |
/****************************************************************** | |
* * | |
* MultiformattedText::MultiformattedText constructor * | |
* * | |
* Initialize member data * | |
* * | |
******************************************************************/ | |
//MultiformattedText::MultiformattedText() : | |
//hwnd_(NULL), | |
// wszText_(NULL), | |
// cTextLength_(0), | |
// pD2DFactory_(NULL), | |
// pRT_(NULL), | |
// pBlackBrush_(NULL), | |
// pDWriteFactory_(NULL), | |
// pTextFormat_(NULL), | |
// pTextLayout_(NULL) | |
//{ | |
//} | |
/****************************************************************** | |
* * | |
* MultiformattedText::~MultiformattedText destructor * | |
* * | |
* Tear down resources * | |
* * | |
******************************************************************/ | |
~this() | |
{ | |
SafeRelease(pD2DFactory_); | |
SafeRelease(pRT_); | |
SafeRelease(pBlackBrush_); | |
SafeRelease(pDWriteFactory_); | |
SafeRelease(pTextFormat_); | |
SafeRelease(pTextLayout_); | |
} | |
/****************************************************************** | |
* * | |
* MultiformattedText::Initialize * | |
* * | |
* Create application window and device-independent resources * | |
* * | |
******************************************************************/ | |
HRESULT Initialize() | |
{ | |
HINSTANCE hInstance = GetModuleHandle(null); | |
WNDCLASSEX wcex; | |
// Get the dpi information. | |
HDC screen = GetDC(null); | |
dpiScaleX_ = GetDeviceCaps(screen, LOGPIXELSX) / 96.0f; | |
dpiScaleY_ = GetDeviceCaps(screen, LOGPIXELSY) / 96.0f; | |
ReleaseDC(null, screen); | |
HRESULT hr = S_OK; | |
ATOM atom; | |
// Register window class. | |
wcex.cbSize = WNDCLASSEX.sizeof; | |
wcex.style = CS_HREDRAW | CS_VREDRAW; | |
wcex.lpfnWndProc = &WndProc; | |
wcex.cbClsExtra = 0; | |
wcex.cbWndExtra = LONG_PTR.sizeof; | |
wcex.hInstance = hInstance; | |
wcex.hbrBackground = NULL; | |
wcex.lpszMenuName = NULL; | |
wcex.hIcon = LoadIcon( | |
NULL, | |
IDI_APPLICATION); | |
wcex.hCursor = LoadCursor( | |
NULL, | |
IDC_ARROW); | |
wcex.lpszClassName = "D2DMultiformattedText".toUTF16z; | |
wcex.hIconSm = LoadIcon( | |
NULL, | |
IDI_APPLICATION | |
); | |
atom = RegisterClassEx( | |
&wcex | |
); | |
hr = atom ? S_OK : E_FAIL; | |
// Create window. | |
hwnd_ = CreateWindow( | |
"D2DMultiformattedText".toUTF16z, | |
"".toUTF16z, | |
WS_OVERLAPPEDWINDOW, | |
0, | |
0, | |
cast(int)(640.0f / dpiScaleX_), | |
cast(int)(480.0f / dpiScaleY_), | |
null, | |
NULL, | |
hInstance, | |
cast(void*)this | |
); | |
if (SUCCEEDED(hr)) | |
{ | |
hr = hwnd_ ? S_OK : E_FAIL; | |
} | |
if (SUCCEEDED(hr)) | |
{ | |
hr = CreateDeviceIndependentResources( | |
); | |
} | |
if (SUCCEEDED(hr)) | |
{ | |
DrawD2DContent(); | |
} | |
if (SUCCEEDED(hr)) | |
{ | |
ShowWindow(hwnd_, SW_SHOWNORMAL); | |
UpdateWindow(hwnd_); | |
} | |
else | |
{ | |
} | |
return hr; | |
} | |
// Process and dispatch messages | |
void RunMessageLoop() | |
{ | |
MSG msg; | |
while (GetMessage(&msg, null, 0, 0)) | |
{ | |
TranslateMessage(&msg); | |
DispatchMessage(&msg); | |
} | |
} | |
/****************************************************************** | |
* * | |
* MultiformattedText::CreateDeviceIndependentResources * | |
* * | |
* This method is used to create resources which are not bound * | |
* to any device. Their lifetime effectively extends for the * | |
* duration of the app. These resources include the Direct2D and * | |
* DirectWrite factories; and a DirectWrite Text Format object * | |
* (used for identifying particular font characteristics) and * | |
* a Direct2D geometry. * | |
* * | |
******************************************************************/ | |
HRESULT CreateDeviceIndependentResources() | |
{ | |
HRESULT hr; | |
// Create Direct2D factory. | |
hr = D2D1CreateFactory( | |
D2D1_FACTORY_TYPE_SINGLE_THREADED, | |
&IID_ID2D1Factory, | |
null, | |
cast(void**)&pD2DFactory_ | |
); | |
// Create a shared DirectWrite factory. | |
if (SUCCEEDED(hr)) | |
{ | |
hr = DWriteCreateFactory( | |
DWRITE_FACTORY_TYPE_SHARED, | |
&IID_IDWriteFactory, | |
cast(IUnknown*)(&pDWriteFactory_) | |
); | |
} | |
// The string to display. | |
wszText_ = "Hello World using DirectWrite!".toUTF16z; | |
cTextLength_ = 32; //(UINT32) wcslen(wszText_); | |
// Create a text format using Gabriola with a font size of 72. | |
// This sets the default font, weight, stretch, style, and locale. | |
if (SUCCEEDED(hr)) | |
{ | |
hr = pDWriteFactory_.CreateTextFormat( | |
"Gabriola".toUTF16z, // 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, | |
72.0f, | |
"en-us".toUTF16z, | |
&pTextFormat_ | |
); | |
} | |
// Center align (horizontally) the text. | |
if (SUCCEEDED(hr)) | |
{ | |
hr = pTextFormat_.SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER); | |
} | |
// Center align (vertically) the text. | |
if (SUCCEEDED(hr)) | |
{ | |
hr = pTextFormat_.SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_CENTER); | |
} | |
// Create a text layout using the text format. | |
if (SUCCEEDED(hr)) | |
{ | |
hr = pDWriteFactory_.CreateTextLayout( | |
wszText_, // The string to be laid out and formatted. | |
cTextLength_, // The length of the string. | |
pTextFormat_, // The text format to apply to the string (contains font information, etc). | |
640.0f, // The width of the layout box. | |
480.0f, // The height of the layout box. | |
&pTextLayout_ // The IDWriteTextLayout interface pointer. | |
); | |
} | |
// Format the "DirectWrite" substring to be of font size 100. | |
if (SUCCEEDED(hr)) | |
{ | |
DWRITE_TEXT_RANGE textRange = {20, // Start index where "DirectWrite" appears. | |
6 }; // Length of the substring "Direct" in "DirectWrite". | |
hr = pTextLayout_.SetFontSize(100.0f, textRange); | |
} | |
// Format the word "DWrite" to be underlined. | |
if (SUCCEEDED(hr)) | |
{ | |
DWRITE_TEXT_RANGE textRange = {20, // Start index where "DirectWrite" appears. | |
11 }; // Length of the substring "DirectWrite". | |
hr = pTextLayout_.SetUnderline(TRUE, textRange); | |
} | |
if (SUCCEEDED(hr)) | |
{ | |
// Format the word "DWrite" to be bold. | |
DWRITE_TEXT_RANGE textRange = {20, | |
11 }; | |
hr = pTextLayout_.SetFontWeight(DWRITE_FONT_WEIGHT_BOLD, textRange); | |
} | |
// Declare a typography pointer. | |
IDWriteTypography pTypography = NULL; | |
// Create a typography interface object. | |
if (SUCCEEDED(hr)) | |
{ | |
hr = pDWriteFactory_.CreateTypography(&pTypography); | |
} | |
// Set the stylistic set. | |
DWRITE_FONT_FEATURE fontFeature = {DWRITE_FONT_FEATURE_TAG_STYLISTIC_SET_7, | |
1}; | |
if (SUCCEEDED(hr)) | |
{ | |
hr = pTypography.AddFontFeature(fontFeature); | |
} | |
if (SUCCEEDED(hr)) | |
{ | |
// Set the typography for the entire string. | |
DWRITE_TEXT_RANGE textRange = {0, | |
cTextLength_}; | |
hr = pTextLayout_.SetTypography(pTypography, textRange); | |
} | |
SafeRelease(pTypography); | |
return hr; | |
} | |
/****************************************************************** | |
* * | |
* MultiformattedText::CreateDeviceResources * | |
* * | |
* This method creates resources which are bound to a particular * | |
* D3D device. It's all centralized here, in case the resources * | |
* need to be recreated in case of D3D device loss (eg. display * | |
* change, remoting, removal of video card, etc). * | |
* * | |
******************************************************************/ | |
HRESULT CreateDeviceResources() | |
{ | |
HRESULT hr = S_OK; | |
RECT rc; | |
GetClientRect( | |
hwnd_, | |
&rc | |
); | |
D2D1_SIZE_U size = D2D1.SizeU( | |
rc.right - rc.left, | |
rc.bottom - rc.top | |
); | |
if (!pRT_) | |
{ | |
// Create a Direct2D render target. | |
hr = pD2DFactory_.CreateHwndRenderTarget( | |
D2D1.RenderTargetPropertiesPtr(), | |
D2D1.HwndRenderTargetPropertiesPtr( | |
hwnd_, | |
size | |
), | |
&pRT_ | |
); | |
// Create a black brush. | |
if (SUCCEEDED(hr)) | |
{ | |
hr = pRT_.CreateSolidColorBrush( | |
&D2D1.ColorF( | |
D2D1.ColorF.Black | |
).color, | |
null, | |
&pBlackBrush_ | |
); | |
} | |
} | |
return hr; | |
} | |
/****************************************************************** | |
* * | |
* MultiformattedText::DiscardDeviceResources * | |
* * | |
* Discard device-specific resources which need to be recreated * | |
* when a D3D device is lost * | |
* * | |
******************************************************************/ | |
void DiscardDeviceResources() | |
{ | |
SafeRelease(pRT_); | |
SafeRelease(pBlackBrush_); | |
} | |
/****************************************************************** | |
* * | |
* MultiformattedText::DrawText * | |
* * | |
* This method will draw text using the IDWriteTextLayout * | |
* via the Direct2D render target * | |
* * | |
******************************************************************/ | |
HRESULT DrawText() | |
{ | |
RECT rc; | |
GetClientRect( | |
hwnd_, | |
&rc | |
); | |
D2D1_POINT_2F origin = D2D1.Point2F( | |
cast(FLOAT)(rc.top / dpiScaleY_), | |
cast(FLOAT)(rc.left / dpiScaleX_) | |
); | |
pRT_.DrawTextLayout( | |
origin, | |
pTextLayout_, | |
pBlackBrush_ | |
); | |
return S_OK; | |
} | |
/****************************************************************** | |
* * | |
* MultiformattedText::DrawD2DContent * | |
* * | |
* This method draws a bitmap a couple times, draws some * | |
* geometry, and writes "Hello World" * | |
* * | |
* Note that this function will not render anything if the window * | |
* is occluded (eg. obscured by other windows or off monitor). * | |
* Also, this function will automatically discard device-specific * | |
* resources if the D3D device disappears during execution, and * | |
* will recreate the resources the next time it's invoked. * | |
* * | |
******************************************************************/ | |
HRESULT DrawD2DContent() | |
{ | |
HRESULT hr; | |
hr = CreateDeviceResources(); | |
if (!(pRT_.CheckWindowState() & D2D1_WINDOW_STATE_OCCLUDED)) | |
{ | |
pRT_.BeginDraw(); | |
D2D1_MATRIX_3X2_F m = D2D1.IdentityMatrix(); | |
pRT_.SetTransform(&m); | |
pRT_.Clear(&D2D1.ColorF(D2D1.ColorF.White).color); | |
// Call the DrawText method of this class. | |
if (SUCCEEDED(hr)) | |
{ | |
hr = DrawText(); | |
} | |
if (SUCCEEDED(hr)) | |
{ | |
hr = pRT_.EndDraw( | |
); | |
} | |
} | |
if (FAILED(hr)) | |
{ | |
DiscardDeviceResources(); | |
} | |
return hr; | |
} | |
/****************************************************************** | |
* * | |
* MultiformattedText::OnResize * | |
* * | |
* If the application receives a WM_SIZE message, this method * | |
* resize the render target appropriately. * | |
* * | |
******************************************************************/ | |
void OnResize(UINT width, UINT height) | |
{ | |
if (pTextLayout_) | |
{ | |
pTextLayout_.SetMaxWidth(cast(FLOAT)(width / dpiScaleX_)); | |
pTextLayout_.SetMaxHeight(cast(FLOAT)(height / dpiScaleY_)); | |
} | |
if (pRT_) | |
{ | |
D2D1_SIZE_U size; | |
size.width = width; | |
size.height = height; | |
pRT_.Resize(&size); | |
} | |
} | |
/****************************************************************** | |
* * | |
* MultiformattedText::WndProc * | |
* * | |
* Window message handler * | |
* * | |
******************************************************************/ | |
extern(Windows) nothrow static | |
LRESULT WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) | |
{ | |
scope (failure) assert(0); | |
if (message == WM_CREATE) | |
{ | |
LPCREATESTRUCT pcs = cast(LPCREATESTRUCT)lParam; | |
MultiformattedText pMultiformattedText = cast(MultiformattedText)pcs.lpCreateParams; | |
SetWindowLongPtr( | |
hwnd, | |
GWLP_USERDATA, | |
cast(long)cast(void*)pMultiformattedText | |
); | |
return 1; | |
} | |
MultiformattedText pMultiformattedText = cast(MultiformattedText)cast(void*)( | |
GetWindowLongPtr( | |
hwnd, | |
GWLP_USERDATA | |
)); | |
if (pMultiformattedText !is null) | |
{ | |
switch(message) | |
{ | |
case WM_SIZE: | |
{ | |
UINT width = LOWORD(lParam); | |
UINT height = HIWORD(lParam); | |
pMultiformattedText.OnResize(width, height); | |
} | |
return 0; | |
case WM_PAINT: goto case; | |
case WM_DISPLAYCHANGE: | |
{ | |
PAINTSTRUCT ps; | |
BeginPaint(hwnd, &ps); | |
pMultiformattedText.DrawD2DContent( | |
); | |
EndPaint( | |
hwnd, | |
&ps | |
); | |
} | |
return 0; | |
case WM_DESTROY: | |
{ | |
PostQuitMessage(0); | |
} | |
return 1; | |
default: | |
break; | |
} | |
} | |
return DefWindowProc( | |
hwnd, | |
message, | |
wParam, | |
lParam | |
); | |
} | |
} | |
extern(Windows) | |
int WinMain( | |
HINSTANCE /* hInstance */, | |
HINSTANCE /* hPrevInstance */, | |
LPSTR /* lpCmdLine */, | |
int /* nCmdShow */ | |
) | |
{ | |
import core.runtime; | |
Runtime.initialize(); | |
if (SUCCEEDED(CoInitialize(null))) | |
{ | |
{ | |
auto app = new MultiformattedText(); | |
if (SUCCEEDED(app.Initialize())) | |
{ | |
app.RunMessageLoop(); | |
} | |
} | |
CoUninitialize(); | |
} | |
Runtime.terminate(); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment