diff --git a/src/text/directwrite.c b/src/text/directwrite.c index f9d8181..9bafe5d 100644 --- a/src/text/directwrite.c +++ b/src/text/directwrite.c @@ -17,6 +17,15 @@ #include "dwrite.h" #include "dwrite_3.h" +typedef struct CustomTextRenderer { + IDWriteTextRendererVtbl *lpVtbl; + LONG refCount; + IDWriteFactory6 * write_factory; + IDWriteBitmapRenderTarget * target; + +} CustomTextRenderer; +static CustomTextRenderer *CustomTextRenderer_Create(IDWriteFactory6 * write_factory, IDWriteBitmapRenderTarget * target); + static struct dw { HMODULE d2d1_lib; HMODULE dwrite_lib; @@ -31,10 +40,7 @@ struct _MwFLFont { MwBool valid; IDWriteFactory6* write_factory; - IDWriteTextFormat* dTextFormat; ID2D1Factory* d2dFactory; - ID2D1HwndRenderTarget* rt; - ID2D1SolidColorBrush* blackBrush; IDWriteGdiInterop *gdiInterop; IDWriteFontFile * file; @@ -42,6 +48,8 @@ struct _MwFLFont { IDWriteFontSetBuilder2 * font_builder; IDWriteFontSet* font_set; IDWriteFontCollection1* font_collection; + IDWriteTextFormat * text_format; + IDWriteTextFormat * text_layout; LOGFONTW logfont; HFONT font; @@ -175,18 +183,23 @@ static void* dw_MwFontLoad(unsigned char* data, unsigned int size, int px) { goto fail; } + + hr = IDWriteFactory6_GetGdiInterop(ttf->write_factory, &ttf->gdiInterop); if(!SUCCEEDED(hr)) { printf("GetGdiInterop failed for TTF (%08lx), cannot use DirectWrite\n",hr); goto fail; } - IDWriteGdiInterop_ConvertFontFaceToLOGFONT(ttf->gdiInterop, ttf->font_face, &ttf->logfont); + 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)) { - printf("IDWriteGdiInterop_ConvertFontFaceToLOGFONT failed for TTF (%08lx), cannot use DirectWrite\n",hr); + printf("IDWriteFactory_CreateTextFormat failed for TTF (%08lx), cannot use DirectWrite\n",hr); goto fail; } + + + ttf->valid = MwTRUE; return ttf; @@ -195,127 +208,44 @@ fail: return ttf; } -static UINT32 *str_to_codepoints(const char *str, UINT codePage, UINT32 *pCount) -{ - int wideLen; - WCHAR *wideBuf; - UINT32 *codePoints; - UINT32 count; - int i; - - if (pCount != NULL) { - *pCount = 0; - } - if (str == NULL) { - return NULL; - } - - /* Size query; result includes the terminating null. */ - wideLen = MultiByteToWideChar(codePage, 0, str, -1, NULL, 0); - if (wideLen <= 0) { - return NULL; - } - - wideBuf = (WCHAR *)malloc((size_t)wideLen * sizeof(WCHAR)); - if (wideBuf == NULL) { - return NULL; - } - - if (MultiByteToWideChar(codePage, 0, str, -1, wideBuf, wideLen) == 0) { - free(wideBuf); - return NULL; - } - - /* One code point per UTF-16 unit is the worst case (no surrogate - pairs), so wideLen is always a safe upper bound for the output. */ - codePoints = (UINT32 *)malloc((size_t)wideLen * sizeof(UINT32)); - if (codePoints == NULL) { - free(wideBuf); - return NULL; - } - - count = 0; - i = 0; - while (wideBuf[i] != 0) { - WCHAR unit = wideBuf[i]; - - if (unit >= 0xD800 && unit <= 0xDBFF && - wideBuf[i + 1] >= 0xDC00 && wideBuf[i + 1] <= 0xDFFF) { - /* High + low surrogate -> one supplementary-plane code point. */ - UINT32 hi = (UINT32)(unit - 0xD800); - UINT32 lo = (UINT32)(wideBuf[i + 1] - 0xDC00); - - codePoints[count] = 0x10000u + (hi << 10) + lo; - count++; - i += 2; - } else { - /* Ordinary BMP character (or a lone/invalid surrogate, passed - through as-is -- GetGlyphIndices will just report .notdef - for it). */ - codePoints[count] = (UINT32)unit; - count++; - i += 1; - } - } - - free(wideBuf); - - if (pCount != NULL) { - *pCount = count; - } - return codePoints; -} static int dw_MwDrawText(MwWidget handle, MwFLFont ttf, MwPoint* point, const char* text, MwLLColor color) { - IDWriteBitmapRenderTarget *target; - RECT rc; - HDC memoryHDC; - DWRITE_GLYPH_RUN glyphRun; - DWRITE_FONT_METRICS metrics; - LONG width, height; - IDWriteRenderingParams * rendering_params; - UINT32 codepoint_len; - UINT32* codepoints; - UINT16 *index; + IDWriteTextLayout * text_layout; + IDWriteBitmapRenderTarget *target = NULL; + HRESULT hr; + RECT rc = {0}; + HDC memoryHDC = NULL; + DWRITE_FONT_METRICS metrics = {0}; + LONG width = 0, height = 0; + size_t wtext_len = strlen(text) * sizeof(UINT32); + WCHAR* wtext = malloc(wtext_len); + CustomTextRenderer * texRenderer; - codepoints = str_to_codepoints(text, CP_UTF8, &codepoint_len); - index = malloc(sizeof(UINT16) * codepoint_len); + MultiByteToWideChar(CP_UTF8, 0, text, -1, wtext, wtext_len); IDWriteFontFace_GetMetrics(ttf->font_face, &metrics); GetClientRect(handle->lowlevel->gdi.hWnd, &rc); MapWindowPoints(handle->lowlevel->gdi.hWnd, GetParent(handle->lowlevel->gdi.hWnd), (LPPOINT)&rc, 2); - IDWriteFactory_CreateRenderingParams(ttf->write_factory, &rendering_params); - - IDWriteFontFace_GetGlyphIndices(ttf->font_face, codepoints, codepoint_len, index); - - glyphRun.fontFace = ttf->font_face; - glyphRun.fontEmSize = (ttf->px*96.f)/72.f; - glyphRun.glyphCount = codepoint_len; - glyphRun.glyphIndices = index; - width = rc.right - rc.left; height = rc.bottom - rc.top; + hr = IDWriteFactory_CreateTextLayout(ttf->write_factory, wtext, wtext_len, ttf->text_format, width, height, &text_layout); + IDWriteGdiInterop_CreateBitmapRenderTarget(ttf->gdiInterop, handle->lowlevel->gdi.hDC, width, height, &target); - IDWriteBitmapRenderTarget_DrawGlyphRun(target, point->x, point->y, DWRITE_MEASURING_MODE_GDI_NATURAL, &glyphRun, rendering_params, RGB(color->common.red, color->common.green, color->common.blue), NULL); + texRenderer = CustomTextRenderer_Create(ttf->write_factory, target); - - memoryHDC = IDWriteBitmapRenderTarget_GetMemoryDC(target); - - BitBlt(handle->lowlevel->gdi.hDC, 0,0, width, height, memoryHDC, 0, 0, SRCCOPY | NOMIRRORBITMAP); + IDWriteTextLayout_Draw(text_layout, target, (IDWriteTextRenderer*)texRenderer, point->x, point->y); IDWriteBitmapRenderTarget_Release(target); - /*SelectObject(handle->lowlevel->gdi.hDC, &ttf->logfont); - TextOut(handle->lowlevel->gdi.hDC, point->x, point->y, text, ttf->px);*/ return 0; } static int dw_MwTextWidth(MwFLFont ttf, const char* text) { - int tw = 1; + int tw = (ttf->px * strlen(text)) / 2; return tw + 1; } @@ -326,7 +256,7 @@ static int dw_MwTextHeight(MwFLFont ttf, int count) { IDWriteFontFace_GetMetrics(ttf->font_face, &metrics); - height = ceil(((float)metrics.capHeight * ttf->px / metrics.designUnitsPerEm) * count); + height = ceil(((float)metrics.capHeight * ttf->px / metrics.designUnitsPerEm) * count) * 2; return height; } @@ -339,6 +269,172 @@ static void dw_MwFontFree(void* handle) { free(ttf); } +/* ---- IUnknown ---- */ + +static HRESULT STDMETHODCALLTYPE CTR_QueryInterface( + IDWriteTextRenderer *iface, REFIID riid, void **ppvObject) +{ + if (!ppvObject) + return E_POINTER; + + if (IsEqualGUID(riid, &IID_IUnknown) || + IsEqualGUID(riid, &IID_IDWritePixelSnapping) || + IsEqualGUID(riid, &IID_IDWriteTextRenderer)) + { + *ppvObject = iface; + iface->lpVtbl->AddRef(iface); + return S_OK; + } + + *ppvObject = NULL; + return E_NOINTERFACE; +} + +static ULONG STDMETHODCALLTYPE CTR_AddRef(IDWriteTextRenderer *iface) +{ + CustomTextRenderer *This = (CustomTextRenderer *)iface; + return InterlockedIncrement(&This->refCount); +} + +static ULONG STDMETHODCALLTYPE CTR_Release(IDWriteTextRenderer *iface) +{ + CustomTextRenderer *This = (CustomTextRenderer *)iface; + LONG ref = InterlockedDecrement(&This->refCount); + if (ref == 0) + HeapFree(GetProcessHeap(), 0, This); + return (ULONG)ref; +} + +/* ---- IDWritePixelSnapping (base interface, treated as "core") ---- */ + +static HRESULT STDMETHODCALLTYPE CTR_IsPixelSnappingDisabled( + IDWriteTextRenderer *iface, void *clientDrawingContext, BOOL *isDisabled) +{ + (void)iface; (void)clientDrawingContext; + if (!isDisabled) + return E_POINTER; + *isDisabled = FALSE; + return S_OK; +} + +static HRESULT STDMETHODCALLTYPE CTR_GetCurrentTransform( + IDWriteTextRenderer *iface, void *clientDrawingContext, DWRITE_MATRIX *transform) +{ + (void)iface; (void)clientDrawingContext; + if (!transform) + return E_POINTER; + + transform->m11 = 1.0f; + transform->m12 = 0.0f; + transform->m21 = 0.0f; + transform->m22 = 1.0f; + transform->dx = 0.0f; + transform->dy = 0.0f; + return S_OK; +} + +static HRESULT STDMETHODCALLTYPE CTR_GetPixelsPerDip( + IDWriteTextRenderer *iface, void *clientDrawingContext, FLOAT *pixelsPerDip) +{ + (void)iface; (void)clientDrawingContext; + if (!pixelsPerDip) + return E_POINTER; + *pixelsPerDip = 1.0f; + return S_OK; +} + +static HRESULT STDMETHODCALLTYPE CTR_DrawGlyphRun( + IDWriteTextRenderer *iface, + void *clientDrawingContext, + FLOAT baselineOriginX, + FLOAT baselineOriginY, + DWRITE_MEASURING_MODE measuringMode, + DWRITE_GLYPH_RUN const *glyphRun, + DWRITE_GLYPH_RUN_DESCRIPTION const *glyphRunDescription, + IUnknown *clientDrawingEffect) +{ + IDWriteRenderingParams *params; + CustomTextRenderer * This = (CustomTextRenderer*)iface; + + IDWriteFactory6_CreateRenderingParams(This->write_factory, ¶ms); + + return IDWriteBitmapRenderTarget_DrawGlyphRun(This->target, baselineOriginX, baselineOriginY, measuringMode, glyphRun, params, RGB(255,255,255), NULL); +} + +static HRESULT STDMETHODCALLTYPE CTR_DrawUnderline( + IDWriteTextRenderer *iface, + void *clientDrawingContext, + FLOAT baselineOriginX, + FLOAT baselineOriginY, + DWRITE_UNDERLINE const *underline, + IUnknown *clientDrawingEffect) +{ + (void)iface; (void)clientDrawingContext; (void)baselineOriginX; + (void)baselineOriginY; (void)underline; (void)clientDrawingEffect; + return E_NOTIMPL; +} + +static HRESULT STDMETHODCALLTYPE CTR_DrawStrikethrough( + IDWriteTextRenderer *iface, + void *clientDrawingContext, + FLOAT baselineOriginX, + FLOAT baselineOriginY, + DWRITE_STRIKETHROUGH const *strikethrough, + IUnknown *clientDrawingEffect) +{ + (void)iface; (void)clientDrawingContext; (void)baselineOriginX; + (void)baselineOriginY; (void)strikethrough; (void)clientDrawingEffect; + return E_NOTIMPL; +} + +static HRESULT STDMETHODCALLTYPE CTR_DrawInlineObject( + IDWriteTextRenderer *iface, + void *clientDrawingContext, + FLOAT originX, + FLOAT originY, + IDWriteInlineObject *inlineObject, + BOOL isSideways, + BOOL isRightToLeft, + IUnknown *clientDrawingEffect) +{ + (void)iface; (void)clientDrawingContext; (void)originX; (void)originY; + (void)inlineObject; (void)isSideways; (void)isRightToLeft; + (void)clientDrawingEffect; + return E_NOTIMPL; +} + +/* ---- Vtable instance ---- */ + +static IDWriteTextRendererVtbl CTR_Vtbl = { + CTR_QueryInterface, + CTR_AddRef, + CTR_Release, + CTR_IsPixelSnappingDisabled, + CTR_GetCurrentTransform, + CTR_GetPixelsPerDip, + CTR_DrawGlyphRun, + CTR_DrawUnderline, + CTR_DrawStrikethrough, + CTR_DrawInlineObject +}; + +/* ---- Constructor ---- */ + +static CustomTextRenderer *CustomTextRenderer_Create(IDWriteFactory6 * write_factory, IDWriteBitmapRenderTarget * target) +{ + CustomTextRenderer *obj = + (CustomTextRenderer *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, + sizeof(CustomTextRenderer)); + if (!obj) + return NULL; + + obj->lpVtbl = &CTR_Vtbl; + obj->refCount = 1; + obj->write_factory = write_factory; + obj->target = target; + return obj; +} + int MWFL_DWSetup(void) { /* Try and load DirectWrite. If it fails, return 1, signifying we default to bitmap drawing. */ dw_table.d2d1_lib = LoadLibrary("D2d1.dll");