Uses DirectWrite and D2D to render Segoe UI Emojis
Last active
November 10, 2023 21:19
-
-
Save nickav/ed3428fb5a230aeaa5170888956a7b22 to your computer and use it in GitHub Desktop.
Win32 Colored Emoji Rendering
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
// | |
// File: win32_emojis.cpp | |
// Compile with: cl win32_emojis.cpp | |
// | |
#define WIN32_LEAN_AND_MEAN | |
#define NOMINMAX | |
#include <windows.h> | |
#include <assert.h> | |
#include <stdio.h> | |
#include <d2d1.h> | |
#include <dwrite.h> | |
#pragma comment(lib, "gdi32.lib") | |
#pragma comment(lib, "user32.lib") | |
#pragma comment(lib, "d2d1.lib") | |
#pragma comment(lib, "dwrite.lib") | |
#define WINDOW_CLASS_NAME L"Window Class" | |
static ID2D1Factory *d2d_factory = NULL; | |
static IDWriteFactory *dw_factory = NULL; | |
static ID2D1HwndRenderTarget *render_target = NULL; | |
static ID2D1SolidColorBrush *brush = NULL; | |
static IDWriteTextFormat *text_format = NULL; | |
void win32_fatal(const char *message) | |
{ | |
MessageBoxA(NULL, message, "Fatal Error", MB_ICONEXCLAMATION | MB_OK); | |
ExitProcess(1); | |
} | |
void DrawEmojis(ID2D1HwndRenderTarget *render_target) | |
{ | |
render_target->BeginDraw(); | |
render_target->Clear(D2D1::ColorF(D2D1::ColorF::White)); | |
WCHAR text[] = L"\U0001F604 \U0001F525 \U0001F422"; | |
UINT32 count = sizeof(text) / sizeof(WCHAR); | |
D2D1_RECT_F rect = D2D1::RectF(50.0f, 50.0f, 200.0f, 200.0f); | |
render_target->DrawText(text, count, text_format, rect, brush, D2D1_DRAW_TEXT_OPTIONS_ENABLE_COLOR_FONT); | |
render_target->EndDraw(); | |
} | |
LRESULT CALLBACK win32_window_callback(HWND hwnd, UINT message, WPARAM w_param, LPARAM l_param) | |
{ | |
LRESULT result = 0; | |
switch (message) | |
{ | |
case WM_CLOSE: | |
{ | |
ExitProcess(0); | |
} break; | |
case WM_PAINT: | |
{ | |
PAINTSTRUCT ps; | |
HDC hdc = BeginPaint(hwnd, &ps); | |
DrawEmojis(render_target); | |
EndPaint(hwnd, &ps); | |
} break; | |
case WM_SIZE: | |
{ | |
UINT width = LOWORD(l_param); | |
UINT height = HIWORD(l_param); | |
if (render_target) | |
{ | |
render_target->Resize(D2D1_SIZE_U{width, height}); | |
} | |
} break; | |
default: | |
{ | |
result = DefWindowProcW(hwnd, message, w_param, l_param); | |
} break; | |
} | |
return result; | |
} | |
int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prev_inst, LPSTR argv, int argc) | |
{ | |
// NOTE(nick): set DPI awareness | |
HMODULE user32 = LoadLibraryA("user32.dll"); | |
// @Incomplete: support older OS versions | |
typedef BOOL Win32_SetProcessDpiAwarenessContext(HANDLE); | |
Win32_SetProcessDpiAwarenessContext *SetProcessDpiAwarenessContext = (Win32_SetProcessDpiAwarenessContext *) GetProcAddress(user32, "SetProcessDpiAwarenessContext"); | |
SetProcessDpiAwarenessContext(((HANDLE) -4) /* DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 */); | |
WNDCLASSEXW wc = {}; | |
wc.cbSize = sizeof(wc); | |
wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; | |
wc.lpfnWndProc = (WNDPROC)win32_window_callback; | |
wc.cbClsExtra = 0; | |
wc.cbWndExtra = 0; | |
wc.hInstance = instance; | |
wc.hIcon = LoadIconA(instance, "APPICON"); | |
wc.hCursor = LoadCursorA(0, IDC_ARROW); | |
wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); | |
wc.lpszMenuName = NULL; | |
wc.lpszClassName = WINDOW_CLASS_NAME; | |
wc.hIconSm = NULL; | |
if (!RegisterClassExW(&wc)) { | |
win32_fatal("Failed to register window class.\n"); | |
} | |
WCHAR *title16 = L"Win32 Color Emoji Rendering Demo"; | |
HWND hwnd = CreateWindowW(WINDOW_CLASS_NAME, title16, WS_OVERLAPPEDWINDOW, | |
CW_USEDEFAULT, CW_USEDEFAULT, 1280, 720, | |
0, 0, instance, 0 | |
); | |
ShowWindow(hwnd, SW_SHOW); | |
// NOTE(nick): set up D2D and DirectWrite state | |
HRESULT hr = 0; | |
hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &d2d_factory); | |
assert(hr == S_OK); | |
D2D1_RENDER_TARGET_PROPERTIES props = D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT, D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_PREMULTIPLIED)); | |
hr = d2d_factory->CreateHwndRenderTarget(props, D2D1::HwndRenderTargetProperties(hwnd, D2D1_SIZE_U{ (UINT32)(1280), (UINT32)(720) }), &render_target); | |
assert(hr == S_OK); | |
hr = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), reinterpret_cast<IUnknown**>(&dw_factory)); | |
assert(hr == S_OK); | |
hr = dw_factory->CreateTextFormat(L"Segoe UI Emoji", NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, 32.0f, L"", &text_format); | |
assert(hr == S_OK); | |
hr = render_target->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Black), &brush); | |
assert(hr == S_OK); | |
while (true) | |
{ | |
for (;;) { | |
MSG msg = {}; | |
if (!PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) { | |
break; | |
} | |
TranslateMessage(&msg); | |
DispatchMessageW(&msg); | |
} | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment