Skip to content

Instantly share code, notes, and snippets.

@nickav
Last active November 10, 2023 21:19
Show Gist options
  • Save nickav/ed3428fb5a230aeaa5170888956a7b22 to your computer and use it in GitHub Desktop.
Save nickav/ed3428fb5a230aeaa5170888956a7b22 to your computer and use it in GitHub Desktop.
Win32 Colored Emoji Rendering

Win32 Colored Emoji Rendering

Uses DirectWrite and D2D to render Segoe UI Emojis

demo

//
// 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