DirectWrite support #28

Merged
IoI_xD merged 13 commits from directwrite into master 2026-07-18 11:39:53 +09:00
2 changed files with 325 additions and 291 deletions
Showing only changes of commit 4ce9ee6730 - Show all commits

properly use QueryInterface to return on platforms w/o DirectWrite
Some checks failed
pyrite-dev/milsko/pipeline/head There was a failure building this commit

IoIxD 2026-07-17 19:30:07 -07:00

View file

@ -312,10 +312,17 @@ static MwWidget MwCreateWidget_Internal(MwClass widget_class, const char* name,
#if defined(USE_STB_TRUETYPE) || defined(USE_FREETYPE2) || defined(USE_DIRECTWRITE) #if defined(USE_STB_TRUETYPE) || defined(USE_FREETYPE2) || defined(USE_DIRECTWRITE)
if(IsFirstVisible(h)) { if(IsFirstVisible(h)) {
font_setup:
h->root_font = MwFontLoad(MwTTFData, MwTTFDataSize); h->root_font = MwFontLoad(MwTTFData, MwTTFDataSize);
h->root_boldfont = MwFontLoad(MwBoldTTFData, MwBoldTTFDataSize); h->root_boldfont = MwFontLoad(MwBoldTTFData, MwBoldTTFDataSize);
h->root_monofont = MwFontLoad(MwMonospaceTTFData, MwMonospaceTTFDataSize); h->root_monofont = MwFontLoad(MwMonospaceTTFData, MwMonospaceTTFDataSize);
h->root_boldmonofont = MwFontLoad(MwBoldMonospaceTTFData, MwBoldMonospaceTTFDataSize); h->root_boldmonofont = MwFontLoad(MwBoldMonospaceTTFData, MwBoldMonospaceTTFDataSize);
if(!h->root_font) {
#ifdef USE_STB_TRUETYPE
MwFL_STBTTSetup();
goto font_setup;
#endif
}
} else } else
#endif #endif
{ {

View file

@ -8,6 +8,7 @@
#include <Mw/Milsko.h> #include <Mw/Milsko.h>
#include <d2d1.h> #include <d2d1.h>
#include <shobjidl.h>
#ifndef WINBOOL #ifndef WINBOOL
#define WINBOOL int #define WINBOOL int
@ -18,219 +19,229 @@
#include "dwrite_3.h" #include "dwrite_3.h"
typedef struct CustomTextRenderer { typedef struct CustomTextRenderer {
IDWriteTextRendererVtbl *lpVtbl; IDWriteTextRendererVtbl* lpVtbl;
LONG refCount; LONG refCount;
IDWriteFactory6 * write_factory; IDWriteFactory6* write_factory;
IDWriteBitmapRenderTarget * target; IDWriteBitmapRenderTarget* target;
int red, blue, green; int red, blue, green;
} CustomTextRenderer; } CustomTextRenderer;
static CustomTextRenderer *CustomTextRenderer_Create(IDWriteFactory6 * write_factory, IDWriteBitmapRenderTarget * target, MwLLColor color); static CustomTextRenderer* CustomTextRenderer_Create(IDWriteFactory6* write_factory, IDWriteBitmapRenderTarget* target, MwLLColor color);
static struct dw { static struct dw {
HMODULE d2d1_lib; HMODULE d2d1_lib;
HMODULE dwrite_lib; HMODULE dwrite_lib;
HRESULT (*D2D1CreateFactory)(D2D1_FACTORY_TYPE factoryType, const IID* const riid, const D2D1_FACTORY_OPTIONS* pFactoryOptions, void** ppIFactory); HRESULT (*D2D1CreateFactory)(D2D1_FACTORY_TYPE factoryType, const IID* const riid, const D2D1_FACTORY_OPTIONS* pFactoryOptions, void** ppIFactory);
HRESULT (*DWriteCreateFactory)(int factoryType, const IID* const iid, IUnknown** factory); HRESULT (*DWriteCreateFactory)(int factoryType, const IID* const iid, IUnknown** factory);
HMODULE kernel32_lib;
} dw_table; } dw_table;
struct _MwFLFont { struct _MwFLFont {
int px; int px;
void * data; void* data;
MwBool valid; MwBool valid;
IDWriteFactory6* write_factory; IDWriteFactory6* write_factory;
ID2D1Factory* d2dFactory; ID2D1Factory* d2dFactory;
IDWriteGdiInterop *gdiInterop; IDWriteGdiInterop* gdiInterop;
IDWriteFontFile* file;
IDWriteFontFile * file; IDWriteFontFace* font_face;
IDWriteFontFace * font_face; IDWriteFontSetBuilder2* font_builder;
IDWriteFontSetBuilder2 * font_builder; IDWriteFontSet* font_set;
IDWriteFontSet* font_set;
IDWriteFontCollection1* font_collection; IDWriteFontCollection1* font_collection;
IDWriteTextFormat * text_format; IDWriteTextFormat* text_format;
IDWriteTextFormat * text_layout;
LOGFONTW logfont; LOGFONTW logfont;
HFONT font; HFONT font;
}; };
static int unpack_ttf_data(WCHAR * temp_path_name, unsigned char* data, unsigned int size) { static int unpack_ttf_data(WCHAR* temp_path_name, unsigned char* data, unsigned int size) {
WCHAR temp_path_dir[MAX_PATH]; WCHAR temp_path_dir[MAX_PATH];
HANDLE tempFile; HANDLE tempFile;
int hr; int hr;
hr = GetTempPath2W(MAX_PATH, temp_path_dir); hr = GetTempPathW(MAX_PATH, temp_path_dir);
if (hr == 0) if(hr == 0) {
{ printf("GetTempPathW failed for TTF, cannot use DirectWrite\n");
printf("GetTempPath2 failed for TTF, cannot use DirectWrite\n"); return 1;
return 1; }
} hr = GetTempFileNameW(temp_path_dir, TEXT(L"MILSKO"), 0, temp_path_name);
hr = GetTempFileNameW(temp_path_dir, TEXT(L"MILSKO"), 0, temp_path_name); if(hr == 0) {
if (hr == 0) printf("GetTempFileName failed for TTF, cannot use DirectWrite\n");
{ return 1;
printf("GetTempFileName failed for TTF, cannot use DirectWrite\n"); }
return 1;
}
tempFile = CreateFileW(temp_path_name, tempFile = CreateFileW(temp_path_name,
GENERIC_WRITE, GENERIC_WRITE,
0, 0,
NULL, NULL,
CREATE_NEW, CREATE_NEW,
FILE_ATTRIBUTE_NORMAL, FILE_ATTRIBUTE_NORMAL,
NULL); NULL);
if (tempFile == INVALID_HANDLE_VALUE) if(tempFile == INVALID_HANDLE_VALUE) {
{ DWORD err = GetLastError();
DWORD err = GetLastError(); if(err == ERROR_FILE_EXISTS) {
if(err == ERROR_FILE_EXISTS) { tempFile = CreateFileW(temp_path_name,
tempFile = CreateFileW(temp_path_name, GENERIC_WRITE,
GENERIC_WRITE, 0,
0, NULL,
NULL, OPEN_ALWAYS,
OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL,
FILE_ATTRIBUTE_NORMAL, NULL);
NULL); if(tempFile != INVALID_HANDLE_VALUE) {
if (tempFile != INVALID_HANDLE_VALUE) goto success;
{ } else {
goto success; err = GetLastError();
} else { }
err = GetLastError(); }
} printf("CreateFile failed for TTF (%ld), cannot use DirectWrite\n", err);
} return 1;
printf("CreateFile failed for TTF (%ld), cannot use DirectWrite\n", err); }
return 1;
}
success: success:
hr = WriteFile(tempFile, data, size, NULL, NULL); hr = WriteFile(tempFile, data, size, NULL, NULL);
if(!hr) { if(!hr) {
printf("WriteFile failed for TTF (%ld), cannot use DirectWrite\n", GetLastError()); printf("WriteFile failed for TTF (%ld), cannot use DirectWrite\n", GetLastError());
return 1; return 1;
} }
CloseHandle(tempFile); CloseHandle(tempFile);
return 0; return 0;
} }
static void* dw_MwFontLoad(unsigned char* data, unsigned int size, int px) { static void* dw_MwFontLoad(unsigned char* data, unsigned int size, int px) {
MwFLFont ttf = malloc(sizeof(*ttf)); MwFLFont ttf = malloc(sizeof(*ttf));
HRESULT hr; HRESULT hr;
WCHAR font_name[MAX_PATH]; WCHAR font_name[MAX_PATH];
D2D1_FACTORY_OPTIONS d2d1_options; D2D1_FACTORY_OPTIONS d2d1_options;
d2d1_options.debugLevel = D2D1_DEBUG_LEVEL_ERROR; d2d1_options.debugLevel = D2D1_DEBUG_LEVEL_ERROR;
#define INTERFACE_CHECK(x, factory) \
do { \
x* rsrc = NULL; \
if(!SUCCEEDED((hr = x##_QueryInterface(factory, &IID_##x, (void**)&rsrc)))) { \
printf("Query for interface " #x " failed (%08lX). Cannot use DirectWrite.\n", hr); \
return NULL; \
}; \
} while(0);
memset(ttf, 0, sizeof(*ttf)); memset(ttf, 0, sizeof(*ttf));
ttf->data = malloc(size); ttf->data = malloc(size);
memcpy(ttf->data, data, size); memcpy(ttf->data, data, size);
ttf->px = px; ttf->px = px;
hr = dw_table.D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &IID_ID2D1Factory, &d2d1_options, (void**)&ttf->d2dFactory); hr = dw_table.D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &IID_ID2D1Factory, &d2d1_options, (void**)&ttf->d2dFactory);
if(!SUCCEEDED(hr)) { if(!SUCCEEDED(hr)) {
printf("D2D1CreateFactory failed for TTF (%08lX), cannot use DirectWrite\n", hr); printf("D2D1CreateFactory failed for TTF (%08lX), cannot use DirectWrite\n", hr);
goto fail; goto fail;
} }
hr = dw_table.DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED,&IID_IDWriteFactory, (struct IUnknown**)&ttf->write_factory); INTERFACE_CHECK(ID2D1Factory, ttf->d2dFactory);
hr = dw_table.DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, &IID_IDWriteFactory, (struct IUnknown**)&ttf->write_factory);
if(!SUCCEEDED(hr)) { if(!SUCCEEDED(hr)) {
printf("DWriteCreateFactory failed for TTF (%08lx), cannot use DirectWrite\n", hr); printf("DWriteCreateFactory failed for TTF (%08lx), cannot use DirectWrite\n", hr);
goto fail; goto fail;
} }
INTERFACE_CHECK(IDWriteFactory6, ttf->write_factory);
if(unpack_ttf_data(font_name, data, size) != 0) { if(unpack_ttf_data(font_name, data, size) != 0) {
goto fail; goto fail;
}; };
hr = IDWriteFactory6_CreateFontFileReference(ttf->write_factory, font_name, NULL, &ttf->file); hr = IDWriteFactory6_CreateFontFileReference(ttf->write_factory, font_name, NULL, &ttf->file);
if(!SUCCEEDED(hr)) { if(!SUCCEEDED(hr)) {
printf("CreateFontFileReference failed for TTF (%08lx), cannot use DirectWrite\n",hr); printf("CreateFontFileReference failed for TTF (%08lx), cannot use DirectWrite\n", hr);
goto fail; goto fail;
} }
INTERFACE_CHECK(IDWriteFontFile, ttf->file);
hr = IDWriteFactory_CreateFontFace(ttf->write_factory, DWRITE_FONT_FACE_TYPE_TRUETYPE, 1, &ttf->file, 0, DWRITE_FONT_SIMULATIONS_NONE, &ttf->font_face); hr = IDWriteFactory_CreateFontFace(ttf->write_factory, DWRITE_FONT_FACE_TYPE_TRUETYPE, 1, &ttf->file, 0, DWRITE_FONT_SIMULATIONS_NONE, &ttf->font_face);
if(!SUCCEEDED(hr)) { if(!SUCCEEDED(hr)) {
printf("IDWriteFactory_CreateFontFace failed for TTF (%08lx), cannot use DirectWrite\n",hr); printf("IDWriteFactory_CreateFontFace failed for TTF (%08lx), cannot use DirectWrite\n", hr);
goto fail; goto fail;
} }
INTERFACE_CHECK(IDWriteFontFace, ttf->font_face);
hr = IDWriteFactory6_CreateFontSetBuilder(ttf->write_factory, &ttf->font_builder); hr = IDWriteFactory6_CreateFontSetBuilder(ttf->write_factory, &ttf->font_builder);
if(!SUCCEEDED(hr)) { if(!SUCCEEDED(hr)) {
printf("IDWriteFactory6_CreateFontSetBuilder failed for TTF (%08lx), cannot use DirectWrite\n",hr); printf("IDWriteFactory6_CreateFontSetBuilder failed for TTF (%08lx), cannot use DirectWrite\n", hr);
goto fail; goto fail;
} }
INTERFACE_CHECK(IDWriteFontSetBuilder2, ttf->font_builder);
hr = IDWriteFontSetBuilder2_AddFontFile(ttf->font_builder, font_name); hr = IDWriteFontSetBuilder2_AddFontFile(ttf->font_builder, font_name);
if(!SUCCEEDED(hr)) { if(!SUCCEEDED(hr)) {
printf("IDWriteFontSetBuilder2_AddFontFile failed for TTF (%08lx), cannot use DirectWrite\n",hr); printf("IDWriteFontSetBuilder2_AddFontFile failed for TTF (%08lx), cannot use DirectWrite\n", hr);
goto fail; goto fail;
} }
hr = IDWriteFontSetBuilder_CreateFontSet(ttf->font_builder, &ttf->font_set); hr = IDWriteFontSetBuilder_CreateFontSet(ttf->font_builder, &ttf->font_set);
if(!SUCCEEDED(hr)) { if(!SUCCEEDED(hr)) {
printf("IDWriteFontSetBuilder_CreateFontSet failed for TTF (%08lx), cannot use DirectWrite\n",hr); printf("IDWriteFontSetBuilder_CreateFontSet failed for TTF (%08lx), cannot use DirectWrite\n", hr);
goto fail; goto fail;
} }
INTERFACE_CHECK(IDWriteFontSet, ttf->font_set);
hr = IDWriteFactory3_CreateFontCollectionFromFontSet(ttf->write_factory, ttf->font_set, &ttf->font_collection); hr = IDWriteFactory3_CreateFontCollectionFromFontSet(ttf->write_factory, ttf->font_set, &ttf->font_collection);
if(!SUCCEEDED(hr)) { if(!SUCCEEDED(hr)) {
printf("IDWriteFactory3_CreateFontCollectionFromFontSet failed for TTF (%08lx), cannot use DirectWrite\n",hr); printf("IDWriteFactory3_CreateFontCollectionFromFontSet failed for TTF (%08lx), cannot use DirectWrite\n", hr);
goto fail; goto fail;
} }
INTERFACE_CHECK(IDWriteFontCollection1, ttf->font_collection);
hr = IDWriteFactory6_GetGdiInterop(ttf->write_factory, &ttf->gdiInterop); hr = IDWriteFactory6_GetGdiInterop(ttf->write_factory, &ttf->gdiInterop);
if(!SUCCEEDED(hr)) { if(!SUCCEEDED(hr)) {
printf("GetGdiInterop failed for TTF (%08lx), cannot use DirectWrite\n",hr); printf("GetGdiInterop failed for TTF (%08lx), cannot use DirectWrite\n", hr);
goto fail; goto fail;
} }
INTERFACE_CHECK(IDWriteGdiInterop, ttf->gdiInterop);
hr = IDWriteFactory_CreateTextFormat(ttf->write_factory, L"milsko font", (IDWriteFontCollection*)ttf->font_collection, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, px, L"en-US", &ttf->text_format); hr = IDWriteFactory_CreateTextFormat(ttf->write_factory, L"milsko font", (IDWriteFontCollection*)ttf->font_collection, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, px, L"en-US", &ttf->text_format);
if(!SUCCEEDED(hr)) { if(!SUCCEEDED(hr)) {
printf("IDWriteFactory_CreateTextFormat failed for TTF (%08lx), cannot use DirectWrite\n",hr); printf("IDWriteFactory_CreateTextFormat failed for TTF (%08lx), cannot use DirectWrite\n", hr);
goto fail; goto fail;
} }
ttf->valid = MwTRUE; ttf->valid = MwTRUE;
return ttf; return ttf;
fail: fail:
ttf->valid = MwFALSE; ttf->valid = MwFALSE;
return ttf; return NULL;
} }
static int dw_MwTextOffset(MwFLFont ttf, const char* text) ; static int dw_MwTextOffset(MwFLFont ttf, const char* text);
static int dw_MwDrawText(MwWidget handle, MwFLFont ttf, MwPoint* point, const char* text, MwLLColor color) { static int dw_MwDrawText(MwWidget handle, MwFLFont ttf, MwPoint* point, const char* text, MwLLColor color) {
IDWriteTextLayout * text_layout; IDWriteTextLayout* text_layout;
IDWriteBitmapRenderTarget *target = NULL; IDWriteBitmapRenderTarget* target = NULL;
HRESULT hr; HRESULT hr;
RECT rc = {0}; RECT rc = {0};
HDC memoryHDC = NULL; HDC memoryHDC = NULL;
DWRITE_FONT_METRICS metrics = {0}; DWRITE_FONT_METRICS metrics = {0};
LONG width = 0, height = 0, offset = 0; LONG width = 0, height = 0, offset = 0;
WCHAR* wtext = NULL; WCHAR* wtext = NULL;
CustomTextRenderer * texRenderer; CustomTextRenderer* texRenderer;
size_t wtext_len = MultiByteToWideChar(CP_UTF8, 0, text, -1, NULL, 0); size_t wtext_len = MultiByteToWideChar(CP_UTF8, 0, text, -1, NULL, 0);
wtext = malloc(wtext_len * 2); /* we get a weird buffer overflow when we try to free this later? allocate well above wtext_len then to prevent that. */ wtext = malloc(wtext_len * 2); /* we get a weird buffer overflow when we try to free this later? allocate well above wtext_len then to prevent that. */
memset(wtext, 0, wtext_len * 2); memset(wtext, 0, wtext_len * 2);
MultiByteToWideChar(CP_UTF8, 0, text, -1, wtext, wtext_len); MultiByteToWideChar(CP_UTF8, 0, text, -1, wtext, wtext_len);
IDWriteFontFace_GetMetrics(ttf->font_face, &metrics); IDWriteFontFace_GetMetrics(ttf->font_face, &metrics);
GetClientRect(handle->lowlevel->gdi.hWnd, &rc); GetClientRect(handle->lowlevel->gdi.hWnd, &rc);
MapWindowPoints(handle->lowlevel->gdi.hWnd, GetParent(handle->lowlevel->gdi.hWnd), (LPPOINT)&rc, 2); MapWindowPoints(handle->lowlevel->gdi.hWnd, GetParent(handle->lowlevel->gdi.hWnd), (LPPOINT)&rc, 2);
width = MwTextWidth(handle,ttf, text); width = MwTextWidth(handle, ttf, text);
height = MwTextHeight(handle, ttf, text); height = MwTextHeight(handle, ttf, text);
offset = dw_MwTextOffset(ttf, text); offset = dw_MwTextOffset(ttf, text);
hr = IDWriteFactory_CreateTextLayout(ttf->write_factory, wtext, wtext_len, ttf->text_format, width, height, &text_layout); hr = IDWriteFactory_CreateTextLayout(ttf->write_factory, wtext, wtext_len, ttf->text_format, width, height, &text_layout);
@ -243,8 +254,8 @@ static int dw_MwDrawText(MwWidget handle, MwFLFont ttf, MwPoint* point, const ch
SetBkMode(handle->lowlevel->gdi.hDC, TRANSPARENT); SetBkMode(handle->lowlevel->gdi.hDC, TRANSPARENT);
BitBlt(memoryHDC, 0, 0, width, height, handle->lowlevel->gdi.hDC, point->x, point->y - (height / 2), SRCCOPY); BitBlt(memoryHDC, 0, 0, width, height, handle->lowlevel->gdi.hDC, point->x, point->y - (height / 2), SRCCOPY);
if (SUCCEEDED(hr)) { if(SUCCEEDED(hr)) {
IDWriteTextLayout_Draw(text_layout, target, (IDWriteTextRenderer*)texRenderer, offset, 0); IDWriteTextLayout_Draw(text_layout, target, (IDWriteTextRenderer*)texRenderer, offset, 0);
} }
BitBlt(handle->lowlevel->gdi.hDC, point->x, point->y - (height / 2), width, height, memoryHDC, 0, 0, SRCCOPY | NOMIRRORBITMAP); BitBlt(handle->lowlevel->gdi.hDC, point->x, point->y - (height / 2), width, height, memoryHDC, 0, 0, SRCCOPY | NOMIRRORBITMAP);
@ -257,21 +268,21 @@ static int dw_MwDrawText(MwWidget handle, MwFLFont ttf, MwPoint* point, const ch
} }
static int dw_MwTextOffset(MwFLFont ttf, const char* text) { static int dw_MwTextOffset(MwFLFont ttf, const char* text) {
IDWriteTextLayout * text_layout; IDWriteTextLayout* text_layout;
DWRITE_FONT_METRICS metrics = {0}; DWRITE_FONT_METRICS metrics = {0};
WCHAR* wtext = NULL; WCHAR* wtext = NULL;
size_t wtext_len = MultiByteToWideChar(CP_UTF8, 0, text, -1, NULL, 0); size_t wtext_len = MultiByteToWideChar(CP_UTF8, 0, text, -1, NULL, 0);
UINT32 * codepoints = malloc(wtext_len * sizeof(UINT32)); UINT32* codepoints = malloc(wtext_len * sizeof(UINT32));
UINT16 * glyphIndices = malloc(wtext_len * sizeof(UINT16)); UINT16* glyphIndices = malloc(wtext_len * sizeof(UINT16));
int i; int i;
DWRITE_GLYPH_METRICS * gmetrics; DWRITE_GLYPH_METRICS* gmetrics;
int tw = 0; int tw = 0;
wtext = malloc(wtext_len * 2); /* we get a weird buffer overflow when we try to free this later? allocate well above wtext_len then to prevent that. */ wtext = malloc(wtext_len * 2); /* we get a weird buffer overflow when we try to free this later? allocate well above wtext_len then to prevent that. */
memset(wtext, 0, wtext_len * 2); memset(wtext, 0, wtext_len * 2);
MultiByteToWideChar(CP_UTF8, 0, text, -1, wtext, wtext_len); MultiByteToWideChar(CP_UTF8, 0, text, -1, wtext, wtext_len);
for(i = 0; i < wtext_len; i++) codepoints[i] = wtext[i]; for(i = 0; i < wtext_len; i++) codepoints[i] = wtext[i];
IDWriteFontFace_GetMetrics(ttf->font_face, &metrics); IDWriteFontFace_GetMetrics(ttf->font_face, &metrics);
@ -284,7 +295,7 @@ static int dw_MwTextOffset(MwFLFont ttf, const char* text) {
IDWriteFontFace_GetDesignGlyphMetrics(ttf->font_face, glyphIndices, wtext_len, gmetrics, FALSE); IDWriteFontFace_GetDesignGlyphMetrics(ttf->font_face, glyphIndices, wtext_len, gmetrics, FALSE);
for(i = 0; i < strlen(text); i++) { for(i = 0; i < strlen(text); i++) {
int add = (gmetrics[i].leftSideBearing) * ((float)ttf->px / (float)metrics.designUnitsPerEm); int add = (gmetrics[i].leftSideBearing) * ((float)ttf->px / (float)metrics.designUnitsPerEm);
tw += add; tw += add;
} }
@ -294,21 +305,21 @@ static int dw_MwTextOffset(MwFLFont ttf, const char* text) {
} }
static int dw_MwTextWidth(MwFLFont ttf, const char* text) { static int dw_MwTextWidth(MwFLFont ttf, const char* text) {
IDWriteTextLayout * text_layout; IDWriteTextLayout* text_layout;
DWRITE_FONT_METRICS metrics = {0}; DWRITE_FONT_METRICS metrics = {0};
WCHAR* wtext = NULL; WCHAR* wtext = NULL;
size_t wtext_len = MultiByteToWideChar(CP_UTF8, 0, text, -1, NULL, 0); size_t wtext_len = MultiByteToWideChar(CP_UTF8, 0, text, -1, NULL, 0);
UINT32 * codepoints = malloc(wtext_len * sizeof(UINT32)); UINT32* codepoints = malloc(wtext_len * sizeof(UINT32));
UINT16 * glyphIndices = malloc(wtext_len * sizeof(UINT16)); UINT16* glyphIndices = malloc(wtext_len * sizeof(UINT16));
int i; int i;
DWRITE_GLYPH_METRICS * gmetrics; DWRITE_GLYPH_METRICS* gmetrics;
int tw = 0; int tw = 0;
wtext = malloc(wtext_len * 2); /* we get a weird buffer overflow when we try to free this later? allocate well above wtext_len then to prevent that. */ wtext = malloc(wtext_len * 2); /* we get a weird buffer overflow when we try to free this later? allocate well above wtext_len then to prevent that. */
memset(wtext, 0, wtext_len * 2); memset(wtext, 0, wtext_len * 2);
MultiByteToWideChar(CP_UTF8, 0, text, -1, wtext, wtext_len); MultiByteToWideChar(CP_UTF8, 0, text, -1, wtext, wtext_len);
for(i = 0; i < wtext_len; i++) codepoints[i] = wtext[i]; for(i = 0; i < wtext_len; i++) codepoints[i] = wtext[i];
IDWriteFontFace_GetMetrics(ttf->font_face, &metrics); IDWriteFontFace_GetMetrics(ttf->font_face, &metrics);
@ -321,7 +332,7 @@ static int dw_MwTextWidth(MwFLFont ttf, const char* text) {
IDWriteFontFace_GetDesignGlyphMetrics(ttf->font_face, glyphIndices, wtext_len, gmetrics, FALSE); IDWriteFontFace_GetDesignGlyphMetrics(ttf->font_face, glyphIndices, wtext_len, gmetrics, FALSE);
for(i = 0; i < strlen(text); i++) { for(i = 0; i < strlen(text); i++) {
int add = (gmetrics[i].advanceWidth + gmetrics[i].leftSideBearing) * ((float)ttf->px / (float)metrics.designUnitsPerEm); int add = (gmetrics[i].advanceWidth + gmetrics[i].leftSideBearing) * ((float)ttf->px / (float)metrics.designUnitsPerEm);
tw += add; tw += add;
} }
@ -331,11 +342,10 @@ static int dw_MwTextWidth(MwFLFont ttf, const char* text) {
} }
static int dw_MwTextHeight(MwFLFont ttf, int count) { static int dw_MwTextHeight(MwFLFont ttf, int count) {
(void)count; (void)count;
return (IDWriteTextFormat2_GetFontSize(ttf->text_format) / 72.0f) * GetDeviceCaps(GetDC(NULL), LOGPIXELSY); return (IDWriteTextFormat2_GetFontSize(ttf->text_format) / 72.0f) * GetDeviceCaps(GetDC(NULL), LOGPIXELSY);
} }
static void dw_MwFontFree(void* handle) { static void dw_MwFontFree(void* handle) {
MwFLFont ttf = handle; MwFLFont ttf = handle;
@ -346,139 +356,144 @@ static void dw_MwFontFree(void* handle) {
/* ---- IUnknown ---- */ /* ---- IUnknown ---- */
static HRESULT STDMETHODCALLTYPE CTR_QueryInterface( static HRESULT STDMETHODCALLTYPE CTR_QueryInterface(
IDWriteTextRenderer *iface, REFIID riid, void **ppvObject) IDWriteTextRenderer* iface, REFIID riid, void** ppvObject) {
{ if(!ppvObject)
if (!ppvObject) return E_POINTER;
return E_POINTER;
if (IsEqualGUID(riid, &IID_IUnknown) || if(IsEqualGUID(riid, &IID_IUnknown) ||
IsEqualGUID(riid, &IID_IDWritePixelSnapping) || IsEqualGUID(riid, &IID_IDWritePixelSnapping) ||
IsEqualGUID(riid, &IID_IDWriteTextRenderer)) IsEqualGUID(riid, &IID_IDWriteTextRenderer)) {
{ *ppvObject = iface;
*ppvObject = iface; iface->lpVtbl->AddRef(iface);
iface->lpVtbl->AddRef(iface); return S_OK;
return S_OK; }
}
*ppvObject = NULL; *ppvObject = NULL;
return E_NOINTERFACE; return E_NOINTERFACE;
} }
static ULONG STDMETHODCALLTYPE CTR_AddRef(IDWriteTextRenderer *iface) static ULONG STDMETHODCALLTYPE CTR_AddRef(IDWriteTextRenderer* iface) {
{ CustomTextRenderer* This = (CustomTextRenderer*)iface;
CustomTextRenderer *This = (CustomTextRenderer *)iface; return InterlockedIncrement(&This->refCount);
return InterlockedIncrement(&This->refCount);
} }
static ULONG STDMETHODCALLTYPE CTR_Release(IDWriteTextRenderer *iface) static ULONG STDMETHODCALLTYPE CTR_Release(IDWriteTextRenderer* iface) {
{ CustomTextRenderer* This = (CustomTextRenderer*)iface;
CustomTextRenderer *This = (CustomTextRenderer *)iface; LONG ref = InterlockedDecrement(&This->refCount);
LONG ref = InterlockedDecrement(&This->refCount); if(ref == 0)
if (ref == 0) HeapFree(GetProcessHeap(), 0, This);
HeapFree(GetProcessHeap(), 0, This); return (ULONG)ref;
return (ULONG)ref;
} }
/* ---- IDWritePixelSnapping (base interface, treated as "core") ---- */ /* ---- IDWritePixelSnapping (base interface, treated as "core") ---- */
static HRESULT STDMETHODCALLTYPE CTR_IsPixelSnappingDisabled( static HRESULT STDMETHODCALLTYPE CTR_IsPixelSnappingDisabled(
IDWriteTextRenderer *iface, void *clientDrawingContext, BOOL *isDisabled) IDWriteTextRenderer* iface, void* clientDrawingContext, BOOL* isDisabled) {
{ (void)iface;
(void)iface; (void)clientDrawingContext; (void)clientDrawingContext;
if (!isDisabled) if(!isDisabled)
return E_POINTER; return E_POINTER;
*isDisabled = FALSE; *isDisabled = FALSE;
return S_OK; return S_OK;
} }
static HRESULT STDMETHODCALLTYPE CTR_GetCurrentTransform( static HRESULT STDMETHODCALLTYPE CTR_GetCurrentTransform(
IDWriteTextRenderer *iface, void *clientDrawingContext, DWRITE_MATRIX *transform) IDWriteTextRenderer* iface, void* clientDrawingContext, DWRITE_MATRIX* transform) {
{ (void)iface;
(void)iface; (void)clientDrawingContext; (void)clientDrawingContext;
if (!transform) if(!transform)
return E_POINTER; return E_POINTER;
transform->m11 = 1.0f; transform->m11 = 1.0f;
transform->m12 = 0.0f; transform->m12 = 0.0f;
transform->m21 = 0.0f; transform->m21 = 0.0f;
transform->m22 = 1.0f; transform->m22 = 1.0f;
transform->dx = 0.0f; transform->dx = 0.0f;
transform->dy = 0.0f; transform->dy = 0.0f;
return S_OK; return S_OK;
} }
static HRESULT STDMETHODCALLTYPE CTR_GetPixelsPerDip( static HRESULT STDMETHODCALLTYPE CTR_GetPixelsPerDip(
IDWriteTextRenderer *iface, void *clientDrawingContext, FLOAT *pixelsPerDip) IDWriteTextRenderer* iface, void* clientDrawingContext, FLOAT* pixelsPerDip) {
{ (void)iface;
(void)iface; (void)clientDrawingContext; (void)clientDrawingContext;
if (!pixelsPerDip) if(!pixelsPerDip)
return E_POINTER; return E_POINTER;
*pixelsPerDip = 1.0f; *pixelsPerDip = 1.0f;
return S_OK; return S_OK;
} }
static HRESULT STDMETHODCALLTYPE CTR_DrawGlyphRun( static HRESULT STDMETHODCALLTYPE CTR_DrawGlyphRun(
IDWriteTextRenderer *iface, IDWriteTextRenderer* iface,
void *clientDrawingContext, void* clientDrawingContext,
FLOAT baselineOriginX, FLOAT baselineOriginX,
FLOAT baselineOriginY, FLOAT baselineOriginY,
DWRITE_MEASURING_MODE measuringMode, DWRITE_MEASURING_MODE measuringMode,
DWRITE_GLYPH_RUN const *glyphRun, DWRITE_GLYPH_RUN const* glyphRun,
DWRITE_GLYPH_RUN_DESCRIPTION const *glyphRunDescription, DWRITE_GLYPH_RUN_DESCRIPTION const* glyphRunDescription,
IUnknown *clientDrawingEffect) IUnknown* clientDrawingEffect) {
{ IDWriteRenderingParams* params;
IDWriteRenderingParams *params; CustomTextRenderer* This = (CustomTextRenderer*)iface;
CustomTextRenderer * This = (CustomTextRenderer*)iface;
(void)clientDrawingContext; (void)clientDrawingContext;
(void)glyphRunDescription; (void)glyphRunDescription;
(void)clientDrawingEffect; (void)clientDrawingEffect;
IDWriteFactory6_CreateRenderingParams(This->write_factory, &params); IDWriteFactory6_CreateRenderingParams(This->write_factory, &params);
return IDWriteBitmapRenderTarget_DrawGlyphRun(This->target, baselineOriginX, baselineOriginY, measuringMode, glyphRun, params, RGB(This->red,This->green,This->blue), NULL); return IDWriteBitmapRenderTarget_DrawGlyphRun(This->target, baselineOriginX, baselineOriginY, measuringMode, glyphRun, params, RGB(This->red, This->green, This->blue), NULL);
} }
static HRESULT STDMETHODCALLTYPE CTR_DrawUnderline( static HRESULT STDMETHODCALLTYPE CTR_DrawUnderline(
IDWriteTextRenderer *iface, IDWriteTextRenderer* iface,
void *clientDrawingContext, void* clientDrawingContext,
FLOAT baselineOriginX, FLOAT baselineOriginX,
FLOAT baselineOriginY, FLOAT baselineOriginY,
DWRITE_UNDERLINE const *underline, DWRITE_UNDERLINE const* underline,
IUnknown *clientDrawingEffect) IUnknown* clientDrawingEffect) {
{ (void)iface;
(void)iface; (void)clientDrawingContext; (void)baselineOriginX; (void)clientDrawingContext;
(void)baselineOriginY; (void)underline; (void)clientDrawingEffect; (void)baselineOriginX;
return E_NOTIMPL; (void)baselineOriginY;
(void)underline;
(void)clientDrawingEffect;
return E_NOTIMPL;
} }
static HRESULT STDMETHODCALLTYPE CTR_DrawStrikethrough( static HRESULT STDMETHODCALLTYPE CTR_DrawStrikethrough(
IDWriteTextRenderer *iface, IDWriteTextRenderer* iface,
void *clientDrawingContext, void* clientDrawingContext,
FLOAT baselineOriginX, FLOAT baselineOriginX,
FLOAT baselineOriginY, FLOAT baselineOriginY,
DWRITE_STRIKETHROUGH const *strikethrough, DWRITE_STRIKETHROUGH const* strikethrough,
IUnknown *clientDrawingEffect) IUnknown* clientDrawingEffect) {
{ (void)iface;
(void)iface; (void)clientDrawingContext; (void)baselineOriginX; (void)clientDrawingContext;
(void)baselineOriginY; (void)strikethrough; (void)clientDrawingEffect; (void)baselineOriginX;
return E_NOTIMPL; (void)baselineOriginY;
(void)strikethrough;
(void)clientDrawingEffect;
return E_NOTIMPL;
} }
static HRESULT STDMETHODCALLTYPE CTR_DrawInlineObject( static HRESULT STDMETHODCALLTYPE CTR_DrawInlineObject(
IDWriteTextRenderer *iface, IDWriteTextRenderer* iface,
void *clientDrawingContext, void* clientDrawingContext,
FLOAT originX, FLOAT originX,
FLOAT originY, FLOAT originY,
IDWriteInlineObject *inlineObject, IDWriteInlineObject* inlineObject,
BOOL isSideways, BOOL isSideways,
BOOL isRightToLeft, BOOL isRightToLeft,
IUnknown *clientDrawingEffect) IUnknown* clientDrawingEffect) {
{ (void)iface;
(void)iface; (void)clientDrawingContext; (void)originX; (void)originY; (void)clientDrawingContext;
(void)inlineObject; (void)isSideways; (void)isRightToLeft; (void)originX;
(void)clientDrawingEffect; (void)originY;
return E_NOTIMPL; (void)inlineObject;
(void)isSideways;
(void)isRightToLeft;
(void)clientDrawingEffect;
return E_NOTIMPL;
} }
/* ---- Vtable instance ---- */ /* ---- Vtable instance ---- */
@ -493,46 +508,58 @@ static IDWriteTextRendererVtbl CTR_Vtbl = {
CTR_DrawGlyphRun, CTR_DrawGlyphRun,
CTR_DrawUnderline, CTR_DrawUnderline,
CTR_DrawStrikethrough, CTR_DrawStrikethrough,
CTR_DrawInlineObject CTR_DrawInlineObject};
};
/* ---- Constructor ---- */ /* ---- Constructor ---- */
static CustomTextRenderer *CustomTextRenderer_Create(IDWriteFactory6 * write_factory, IDWriteBitmapRenderTarget * target, MwLLColor color) static CustomTextRenderer* CustomTextRenderer_Create(IDWriteFactory6* write_factory, IDWriteBitmapRenderTarget* target, MwLLColor color) {
{ CustomTextRenderer* obj =
CustomTextRenderer *obj = (CustomTextRenderer*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
(CustomTextRenderer *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CustomTextRenderer));
sizeof(CustomTextRenderer)); if(!obj)
if (!obj) return NULL;
return NULL;
obj->lpVtbl = &CTR_Vtbl; obj->lpVtbl = &CTR_Vtbl;
obj->refCount = 1; obj->refCount = 1;
obj->write_factory = write_factory; obj->write_factory = write_factory;
obj->target = target; obj->target = target;
obj->red = color->common.red; obj->red = color->common.red;
obj->green = color->common.green; obj->green = color->common.green;
obj->blue = color->common.blue; obj->blue = color->common.blue;
return obj; return obj;
} }
int MWFL_DWSetup(void) { int MWFL_DWSetup(void) {
HRESULT hr;
CoInitialize(NULL);
/* Try and load DirectWrite. If it fails, return 1, signifying we default to bitmap drawing. */ /* Try and load DirectWrite. If it fails, return 1, signifying we default to bitmap drawing. */
dw_table.d2d1_lib = LoadLibrary("D2d1.dll"); dw_table.d2d1_lib = LoadLibrary("D2d1.dll");
if(!dw_table.d2d1_lib) { if(!dw_table.d2d1_lib) {
printf("D2d1.dll not found, cannot use DirectWrite\n"); printf("D2d1.dll not found, cannot use DirectWrite\n");
return 1; return 1;
} }
dw_table.dwrite_lib = LoadLibrary("dwrite.dll"); dw_table.dwrite_lib = LoadLibrary("dwrite.dll");
if(!dw_table.dwrite_lib) { if(!dw_table.dwrite_lib) {
printf("dwrite.dll not found, cannot use DirectWrite\n"); printf("dwrite.dll not found, cannot use DirectWrite\n");
return 1; return 1;
} }
#define D2D1_LOAD(x) dw_table.x = (typeof(dw_table.x))GetProcAddress(dw_table.d2d1_lib, #x); if(!dw_table.x) {printf("Can't use DirectWrite, "#x " not found."); return 1;} #define D2D1_LOAD(x) \
dw_table.x = (typeof(dw_table.x))GetProcAddress(dw_table.d2d1_lib, #x); \
if(!dw_table.x) { \
printf("Can't use DirectWrite, " #x " not found."); \
return 1; \
}
#define DWRITE_LOAD(x) dw_table.x = (typeof(dw_table.x))GetProcAddress(dw_table.dwrite_lib, #x); if(!dw_table.x) {printf("Can't use DirectWrite, "#x " not found."); return 1;} #define DWRITE_LOAD(x) \
dw_table.x = (typeof(dw_table.x))GetProcAddress(dw_table.dwrite_lib, #x); \
if(!dw_table.x) { \
printf("Can't use DirectWrite, " #x " not found."); \
return 1; \
}
D2D1_LOAD(D2D1CreateFactory); D2D1_LOAD(D2D1CreateFactory);
DWRITE_LOAD(DWriteCreateFactory); DWRITE_LOAD(DWriteCreateFactory);