As a matter of comparison here is the code to draw a circular text using the D2D API
#include <windows.h>
#include <d2d1.h>
#include <dwrite.h>
#include <cmath>
#pragma comment(lib, "d2d1.lib")
#pragma comment(lib, "dwrite.lib")
constexpr auto IDI_ICON1 = 101;
// Global pointers for Direct2D and DirectWrite interfaces
ID2D1Factory* pD2DFactory = nullptr;
ID2D1HwndRenderTarget* pRenderTarget = nullptr;
ID2D1SolidColorBrush* pBrush = nullptr;
IDWriteFactory* pDWriteFactory = nullptr;
IDWriteTextFormat* pTextFormat = nullptr;
static void Initialize() {
D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &pD2DFactory);
DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), reinterpret_cast<IUnknown**>(&pDWriteFactory));
}
static void CreateGraphicsResources(HWND hwnd) {
if (!pRenderTarget) {
RECT rc;
GetClientRect(hwnd, &rc);
D2D1_SIZE_U size = D2D1::SizeU(rc.right, rc.bottom);
pD2DFactory->CreateHwndRenderTarget(D2D1::RenderTargetProperties(), D2D1::HwndRenderTargetProperties(hwnd, size), &pRenderTarget);
pRenderTarget->CreateSolidColorBrush(D2D1::ColorF(0xFF037BFA), &pBrush);
pDWriteFactory->CreateTextFormat(
L"Segoe GUI emoji",
nullptr,
DWRITE_FONT_WEIGHT_BOLD,
DWRITE_FONT_STYLE_NORMAL,
DWRITE_FONT_STRETCH_NORMAL,
50.0f,
L"", //locale
&pTextFormat
);
pTextFormat->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER);
pTextFormat->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_CENTER);
}
}
static void DiscardGraphicsResources() {
if (pBrush) pBrush->Release();
if (pRenderTarget) pRenderTarget->Release();
if (pTextFormat) pTextFormat->Release();
pBrush = nullptr;
pRenderTarget = nullptr;
pTextFormat = nullptr;
}
static void OnPaint(HWND hwnd) {
PAINTSTRUCT ps;
BeginPaint(hwnd, &ps);
CreateGraphicsResources(hwnd);
pRenderTarget->BeginDraw();
pRenderTarget->Clear(D2D1::ColorF(D2D1::ColorF::White));
D2D1_SIZE_F size = pRenderTarget->GetSize();
const WCHAR* text = L"This is a Circular text using the D2D API.";
const float radius = 200.0f;
const D2D1_POINT_2F center = D2D1::Point2F(size.width / 2, size.height / 2);
const size_t length = wcslen(text);
const float angleStep = 360.0f / static_cast<float>(length);
for (size_t i = 0; i < length; ++i) {
WCHAR letter[2] = { text[i], 0 };
float angle = DegreesToRadians(angleStep * i);
D2D1_POINT_2F position = {
center.x + cos(angle) * radius,
center.y + sin(angle) * radius
};
D2D1_MATRIX_3X2_F rotation = D2D1::Matrix3x2F::Rotation(angleStep * i + 90, position);
pRenderTarget->SetTransform(rotation);
pRenderTarget->DrawText(
letter,
ARRAYSIZE(letter),
pTextFormat,
D2D1::RectF(position.x - 20, position.y - 20, position.x + 20, position.y + 20),
pBrush
);
pRenderTarget->SetTransform(D2D1::Matrix3x2F::Identity());
}
HRESULT hr = pRenderTarget->EndDraw();
if (FAILED(hr) || hr == D2DERR_RECREATE_TARGET) {
DiscardGraphicsResources();
}
EndPaint(hwnd, &ps);
}
static LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_PAINT:
OnPaint(hwnd);
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow) {
Initialize();
// Register the window class
const wchar_t CLASS_NAME[] = L"Sample Window Class";
WNDCLASS wc = {};
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON1));
wc.lpszClassName = CLASS_NAME;
RegisterClass(&wc);
// Create the window
HWND hwnd = CreateWindowEx(
0,
CLASS_NAME,
L"Circular Text with Direct2D",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 600, 600,
nullptr,
nullptr,
hInstance,
nullptr
);
if (hwnd == nullptr) {
return 0;
}
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
// Run the message loop
MSG msg = {};
while (GetMessage(&msg, nullptr, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
DiscardGraphicsResources();
if (pD2DFactory) pD2DFactory->Release();
if (pDWriteFactory) pDWriteFactory->Release();
return 0;
}