From b388b2df09b5301ca411d3ea15aebe1e7519158e Mon Sep 17 00:00:00 2001 From: Martin Mitas Date: Sun, 17 Feb 2019 00:19:31 +0100 Subject: [PATCH 1/7] init.c: Refactor. We need to redo the initialization because WinDrawLib interface modules do not match 1:1 to the implementation modules, from now on. In particular, with the D2D1 back-end, we use DWrite both for the string API and (new not yet implemented) advanced text API. The reason for disntinguishing string and text API is that later, we shall likely implement the text API also for GDI+ back-end using Uniscribe, but the simple string API does not need to load it. --- src/init.c | 206 +++++++++++++++++++++++------------------------------ 1 file changed, 88 insertions(+), 118 deletions(-) diff --git a/src/init.c b/src/init.c index 3dc355c..1e87547 100644 --- a/src/init.c +++ b/src/init.c @@ -1,6 +1,6 @@ /* * WinDrawLib - * Copyright (c) 2016 Martin Mitas + * Copyright (c) 2016-2019 Martin Mitas * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -45,7 +45,7 @@ wdPreInitialize(void (*fnLock)(void), void (*fnUnlock)(void), DWORD dwFlags) static int -wd_init_core_api(void) +core_init(void) { if(!(wd_preinit_flags & WD_DISABLE_D2D)) { if(d2d_init() == 0) @@ -61,7 +61,7 @@ wd_init_core_api(void) } static void -wd_fini_core_api(void) +core_fini(void) { if(d2d_enabled()) d2d_fini(); @@ -69,156 +69,126 @@ wd_fini_core_api(void) gdix_fini(); } -static int -wd_init_image_api(void) -{ - if(d2d_enabled()) { - return wic_init(); - } else { - /* noop */ - return 0; - } -} +static UINT core_counter = 0; +static UINT wic_counter = 0; +static UINT dwrite_counter = 0; -static void -wd_fini_image_api(void) -{ - if(d2d_enabled()) { - wic_fini(); - } else { - /* noop */ - } -} +#define WD_MOD_WIC 0x0001 +#define WD_MOD_DWRITE 0x0002 -static int -wd_init_string_api(void) -{ - if(d2d_enabled()) { - return dwrite_init(); - } else { - /* noop */ - return 0; - } -} +static void wdTerminateImpl(DWORD dwModMask); -static void -wd_fini_string_api(void) +BOOL +wdInitialize(DWORD dwFlags) { - if(d2d_enabled()) { - dwrite_fini(); - } else { - /* noop */ + int err = -1; + DWORD undo = 0; + + wd_lock(); + + /* Core module */ + if(core_counter == 0) { + if(core_init() != 0) + goto out; } -} + core_counter++; + /* WIC module */ + if(d2d_enabled() && (dwFlags & WD_INIT_IMAGEAPI)) { + if(wic_counter == 0) { + if(wic_init() != 0) + goto out; + } -static const struct { - int (*fn_init)(void); - void (*fn_fini)(void); -} wd_modules[] = { - { wd_init_core_api, wd_fini_core_api }, - { wd_init_image_api, wd_fini_image_api }, - { wd_init_string_api, wd_fini_string_api } -}; + wic_counter++; + undo |= WD_MOD_WIC; + } -#define WD_MOD_COUNT (sizeof(wd_modules) / sizeof(wd_modules[0])) + /* DWrite module */ + if(d2d_enabled() && (dwFlags & (WD_INIT_STRINGAPI | WD_INIT_TEXTAPI))) { + if(dwrite_counter == 0) { + if(dwrite_init() != 0) + goto out; + } -#define WD_MOD_COREAPI 0 -#define WD_MOD_IMAGEAPI 1 -#define WD_MOD_STRINGAPI 2 + dwrite_counter++; + undo |= WD_MOD_DWRITE; + } -static UINT wd_init_counter[WD_MOD_COUNT] = { 0 }; + if(gdix_enabled() && (dwFlags & WD_INIT_TEXTAPI)) { + WD_TRACE("wdInitialize: WD_INIT_TEXTAPI for GDI+ back-end is not (yet?) implemented."); + goto out; + } + /* Success. */ + err = 0; -BOOL -wdInitialize(DWORD dwFlags) -{ - BOOL want_init[WD_MOD_COUNT]; - int i; +out: + /* In case of an error, undo what we have done. */ + if(err != 0 && undo != 0) + wdTerminateImpl(undo); - want_init[WD_MOD_COREAPI] = TRUE; - want_init[WD_MOD_IMAGEAPI] = (dwFlags & WD_INIT_IMAGEAPI); - want_init[WD_MOD_STRINGAPI] = (dwFlags & WD_INIT_STRINGAPI); + wd_unlock(); - wd_lock(); + return (err == 0); +} + +static void +wdTerminateImpl(DWORD dwModMask) +{ + /* Handle the optional modules. */ + if((dwModMask & WD_MOD_WIC) && --wic_counter == 0) + wic_fini(); - for(i = 0; i < WD_MOD_COUNT; i++) { - if(!want_init[i]) - continue; + if((dwModMask & WD_MOD_DWRITE) && --dwrite_counter == 0) + dwrite_fini(); - wd_init_counter[i]++; - if(wd_init_counter[i] > 0) { - if(wd_modules[i].fn_init() != 0) - goto fail; + /* Handle the core module. + * + * Note that if the core module counter drops to zero, we make sure no + * optional module survives. This should only happen when that caller + * forgot to pass some flags into wdTerminate() so it does not match + * those passed previously into wdInitialize(). + */ + if(--core_counter == 0) { + if(wic_counter > 0) { + WD_TRACE("wdTerminate: Forcefully terminating WIC module."); + wic_fini(); + wic_counter = 0; } - } - - wd_unlock(); - return TRUE; - -fail: - /* Undo initializations from successful iterations. */ - while(--i >= 0) { - if(want_init[i]) { - wd_init_counter[i]--; - if(wd_init_counter[i] == 0) - wd_modules[i].fn_fini(); + if(dwrite_counter > 0) { + WD_TRACE("wdTerminate: Forcefully terminating DWrite module."); + dwrite_fini(); + dwrite_counter = 0; } - } - wd_unlock(); - return FALSE; + core_fini(); + } } void wdTerminate(DWORD dwFlags) { - BOOL want_fini[WD_MOD_COUNT]; - int i; + DWORD dwModMask = 0; - want_fini[WD_MOD_COREAPI] = TRUE; - want_fini[WD_MOD_IMAGEAPI] = (dwFlags & WD_INIT_IMAGEAPI); - want_fini[WD_MOD_STRINGAPI] = (dwFlags & WD_INIT_STRINGAPI); + if(d2d_enabled() && (dwFlags & WD_INIT_IMAGEAPI)) + dwModMask |= WD_MOD_WIC; + if(d2d_enabled() && (dwFlags & (WD_INIT_STRINGAPI | WD_INIT_TEXTAPI))) + dwModMask |= WD_MOD_DWRITE; wd_lock(); - - for(i = WD_MOD_COUNT-1; i >= 0; i--) { - if(!want_fini[i]) - continue; - - wd_init_counter[i]--; - if(wd_init_counter[i] == 0) - wd_modules[i].fn_fini(); - } - - /* If core module counter has dropped to zero, caller likely forgot to - * terminate some optional module (i.e. mismatching flags for wdTerminate() - * somewhere. So lets kill all those modules forcefully now anyway even - * though well behaving applications should never do that... - */ - if(wd_init_counter[WD_MOD_COREAPI] == 0) { - for(i = WD_MOD_COUNT-1; i >= 0; i--) { - if(wd_init_counter[i] > 0) { - WD_TRACE("wdTerminate: Forcefully terminating module %d.", i); - wd_modules[i].fn_fini(); - wd_init_counter[i] = 0; - } - } - } - + wdTerminateImpl(dwModMask); wd_unlock(); } int wdBackend(void) { - if(d2d_enabled()) { + if(d2d_enabled()) return WD_BACKEND_D2D; - } - if(gdix_enabled()) { + if(gdix_enabled()) return WD_BACKEND_GDIPLUS; - } - return -1; + return -1; } From 11055bd43b5c6953f69d2356929822298537c58c Mon Sep 17 00:00:00 2001 From: Martin Mitas Date: Sun, 17 Feb 2019 00:23:32 +0100 Subject: [PATCH 2/7] Very partial and incomplete implementation of the new text api (D2D1 backend only). --- examples/CMakeLists.txt | 3 + examples/draw-text.c | 180 ++++++++++++++++++++++++++++++++++++++++ include/wdl.h | 100 +++++++++++++++++++++- src/CMakeLists.txt | 1 + src/dummy/dwrite.h | 24 ++++-- src/text.c | 163 ++++++++++++++++++++++++++++++++++++ 6 files changed, 463 insertions(+), 8 deletions(-) create mode 100644 examples/draw-text.c create mode 100644 src/text.c diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 4638c9b..a778716 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -32,6 +32,9 @@ target_link_libraries("draw-simple" "windrawlib") add_executable("draw-string" WIN32 "draw-string.c") target_link_libraries("draw-string" "windrawlib") +add_executable("draw-text" WIN32 "draw-text.c") +target_link_libraries("draw-text" "windrawlib") + add_executable("draw-styled" WIN32 "draw-styled.c") target_link_libraries("draw-styled" "windrawlib") diff --git a/examples/draw-text.c b/examples/draw-text.c new file mode 100644 index 0000000..6cd25ca --- /dev/null +++ b/examples/draw-text.c @@ -0,0 +1,180 @@ + +#include +#include + +#include + + +static HWND hwndMain = NULL; + + + +static WCHAR pszTitle[] = L"Lorem Ipsum"; +static WCHAR pszText[] = + L"Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Etiam dui " + L"sem, fermentum vitae, sagittis id, malesuada in, quam. Morbi imperdiet, " + L"mauris ac auctor dictum, nisl ligula egestas nulla, et sollicitudin " + L"sem purus in lacus. Fusce aliquam vestibulum ipsum. Excepteur sint " + L"occaecat cupidatat non proident, sunt in culpa qui officia deserunt " + L"mollit anim id est laborum. Morbi imperdiet, mauris ac auctor dictum, " + L"nisl ligula egestas nulla, et sollicitudin sem purus in lacus. " + L"Excepteur sint occaecat cupidatat non proident, sunt in culpa qui " + L"officia deserunt mollit anim id est laborum. Et harum quidem rerum " + L"facilis est et expedita distinctio. Ut enim ad minim veniam, quis " + L"nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo " + L"consequat. Donec vitae arcu. Vivamus ac leo pretium faucibus.\n" + L"\n" + L"In dapibus augue non sapien. Aenean id metus id velit ullamcorper " + L"pulvinar. Sed convallis magna eu sem. Maecenas sollicitudin. Nulla " + L"turpis magna, cursus sit amet, suscipit a, interdum id, felis. Nulla " + L"non lectus sed nisl molestie malesuada. Aliquam ante. Integer lacinia. " + L"Nullam sapien sem, ornare ac, nonummy non, lobortis a enim. Integer " + L"lacinia. Mauris metus. Fusce suscipit libero eget elit.\n" + L"\n" + L"Nam quis nulla. Fusce nibh. Nulla pulvinar eleifend sem. Curabitur " + L"sagittis hendrerit ante. Cras pede libero, dapibus nec, pretium sit " + L"amet, tempor quis. Nulla non lectus sed nisl molestie malesuada. " + L"Maecenas sollicitudin. Duis condimentum augue id magna semper rutrum. " + L"Proin mattis lacinia justo. Ut enim ad minima veniam, quis nostrum " + L"exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid " + L"ex ea commodi consequatur? Integer in sapien. Nemo enim ipsam " + L"voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia " + L"consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. " + L"Fusce aliquam vestibulum ipsum. In enim a arcu imperdiet malesuada. " + L"Maecenas ipsum velit, consectetuer eu lobortis ut, dictum at dui.\n" + L"\n" + L"Duis viverra diam non justo. Class aptent taciti sociosqu ad litora " + L"torquent per conubia nostra, per inceptos hymenaeos. Mauris metus. " + L"Nam libero tempore, cum soluta nobis est eligendi optio cumque nihil " + L"impedit quo minus plus id quod maxime placeat facere possimus, omnis " + L"voluptas assumenda est, omnis dolor repellendus. Class aptent taciti " + L"sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos."; + +static void +PaintToCanvas(WD_HCANVAS hCanvas) +{ + RECT client; + WD_HBRUSH hBrush; + WD_HFONT hFont; + WD_RECT rect; + WD_HTEXT hTitle; + WD_HTEXT hText; + WD_FONTMETRICS fontMetrics; + WD_TEXTMETRICS titleMetrics; + + GetClientRect(hwndMain, &client); + rect.x0 = 10.0f; + rect.x1 = client.right - 10.0f; + + wdBeginPaint(hCanvas); + wdClear(hCanvas, WD_RGB(255,255,255)); + hBrush = wdCreateSolidBrush(hCanvas, WD_RGB(0,0,0)); + hFont = wdCreateFontWithGdiHandle(GetStockObject(DEFAULT_GUI_FONT)); + wdFontMetrics(hFont, &fontMetrics); + + /* Create and paint title. */ + rect.y0 = 10.0f; + rect.y1 = client.bottom - 10.0f; + hTitle = wdCreateText(hFont, &rect, pszTitle, -1, 0); + wdSetTextFontSize(hTitle, 0, wcslen(pszTitle), 1.66f * fontMetrics.fEmHeight); + wdSetTextFontSize(hTitle, 0, 1, 1.85f * fontMetrics.fEmHeight); + wdSetTextFontSize(hTitle, 6, 1, 1.85f * fontMetrics.fEmHeight); + wdTextMetrics(hTitle, &titleMetrics); + wdDrawText(hCanvas, hTitle, hBrush, rect.x0, rect.y0, 0); + + /* Create body text, customize some its parts, and paint it. */ + rect.y0 = 20.0f + titleMetrics.fHeight; + rect.y1 = client.bottom - 10.0f; + hText = wdCreateText(hFont, &rect, pszText, -1, 0); + wdSetTextFontWeight(hText, 103, 12, FW_BOLD); + wdSetTextFontStyle(hText, 932, 68, WD_TEXTSTYLE_ITALIC); + wdSetTextStrikethrough(hText, 2159, 5, TRUE); + wdSetTextUnderline(hText, 2165, 4, TRUE); + wdDrawText(hCanvas, hText, hBrush, rect.x0, rect.y0, 0); + + wdDestroyText(hTitle); + wdDestroyText(hText); + wdDestroyBrush(hBrush); + wdDestroyFont(hFont); + wdEndPaint(hCanvas); + } + +/* Main window procedure */ +static LRESULT CALLBACK +MainWinProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch(uMsg) { + case WM_PAINT: + { + PAINTSTRUCT ps; + WD_HCANVAS hCanvas; + + BeginPaint(hwndMain, &ps); + hCanvas = wdCreateCanvasWithPaintStruct(hwndMain, &ps, 0); + PaintToCanvas(hCanvas); + wdDestroyCanvas(hCanvas); + EndPaint(hwndMain, &ps); + return 0; + } + + case WM_PRINTCLIENT: + { + HDC dc = (HDC) wParam; + WD_HCANVAS hCanvas; + + hCanvas = wdCreateCanvasWithHDC(dc, NULL, 0); + PaintToCanvas(hCanvas); + wdDestroyCanvas(hCanvas); + return 0; + } + + case WM_DESTROY: + PostQuitMessage(0); + return 0; + } + + return DefWindowProc(hwnd, uMsg, wParam, lParam); +} + + +int APIENTRY +_tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) +{ + WNDCLASS wc = { 0 }; + MSG msg; + + wdInitialize(WD_INIT_TEXTAPI); + + /* Register main window class */ + wc.lpfnWndProc = MainWinProc; + wc.hInstance = hInstance; + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1); + wc.lpszClassName = _T("main_window"); + wc.style = CS_HREDRAW | CS_VREDRAW; + RegisterClass(&wc); + + /* Create main window */ + hwndMain = CreateWindow( + _T("main_window"), _T("LibWinDraw Example: Draw Text"), + WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 500, 650, + NULL, NULL, hInstance, NULL + ); + SendMessage(hwndMain, WM_SETFONT, (WPARAM) GetStockObject(DEFAULT_GUI_FONT), + MAKELPARAM(TRUE, 0)); + ShowWindow(hwndMain, nCmdShow); + + /* Message loop */ + while(GetMessage(&msg, NULL, 0, 0)) { + if(IsDialogMessage(hwndMain, &msg)) + continue; + + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + wdTerminate(WD_INIT_TEXTAPI); + + /* Return exit code of WM_QUIT */ + return (int)msg.wParam; +} diff --git a/include/wdl.h b/include/wdl.h index f795ce7..c82e62e 100644 --- a/include/wdl.h +++ b/include/wdl.h @@ -1,6 +1,6 @@ /* * WinDrawLib - * Copyright (c) 2015-2018 Martin Mitas + * Copyright (c) 2015-2019 Martin Mitas * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -145,6 +145,7 @@ void wdPreInitialize(void (*fnLock)(void), void (*fnUnlock)(void), DWORD dwFlags #define WD_INIT_COREAPI 0x0000 #define WD_INIT_IMAGEAPI 0x0001 #define WD_INIT_STRINGAPI 0x0002 +#define WD_INIT_TEXTAPI 0x0004 BOOL wdInitialize(DWORD dwFlags); void wdTerminate(DWORD dwFlags); @@ -169,6 +170,7 @@ typedef struct WD_FONT_tag *WD_HFONT; typedef struct WD_IMAGE_tag *WD_HIMAGE; typedef struct WD_CACHEDIMAGE_tag* WD_HCACHEDIMAGE; typedef struct WD_PATH_tag *WD_HPATH; +typedef struct WD_HTEXT_tag *WD_HTEXT; /*************************** @@ -584,8 +586,13 @@ void wdBitBltHICON(WD_HCANVAS hCanvas, HICON hIcon, /* Functions for basic string output. Note the functions operate strictly with * Unicode strings. * + * This API allows to output simple string, using one font, one brush and one + * style. If you need to render more complex text (e.g. a paragraph, which + * uses different styles or fonts or brushes for various parts of the text) + * use rather the advanced text output API below. + * * All these functions are usable only if the library has been initialized with - * the flag WD_INIT_DRAWSTRINGAPI. + * the flag WD_INIT_STRINGAPI. */ /* Flags specifying alignment and various rendering options. @@ -621,6 +628,95 @@ void wdMeasureString(WD_HCANVAS hCanvas, WD_HFONT hFont, const WD_RECT* pRect, float wdStringWidth(WD_HCANVAS hCanvas, WD_HFONT hFont, const WCHAR* pszText); float wdStringHeight(WD_HFONT hFont, const WCHAR* pszText); + +/****************************** + *** Advanced Text Output *** + ******************************/ + +/* Functions for advanced string output. + * Note the functions operate strictly with Unicode strings. + * + * This API is more complex and more difficult to use then wdDrawString(), but + * it offers richer functionality. For example it allows to render paragraph of + * text, whose parts use different fonts, effects, or brushes. + * + * Consider for example paragraph of a text of normal text which includes some + * hyper-links and/or some emphasis of text using a bold or italic typeface + * and/or talks some about some programming language and the described + * identifier should be rendered in a monospaced font. + * + * All these functions are usable only if the library has been initialized with + * the flag WD_INIT_TEXTAPI. + * + * WARNING: This module is currently available only for D2D back-end. If not + * available, wdInitialize(WD_INIT_TEXTAPI) shall fail. + */ + +/* Flags specifying alignment and various rendering options. + * + * Most of the flags are for wdCreateText(). + * Only WD_TEXT_NOCLIP is for wdDrawText(). + */ +#define WD_TEXT_LEFTALIGN WD_STR_LEFTALIGN +#define WD_TEXT_CENTERALIGN WD_STR_CENTERALIGN +#define WD_TEXT_RIGHTALIGN WD_STR_RIGHTALIGN +#define WD_TEXT_TOPALIGN WD_STR_TOPALIGN +#define WD_TEXT_MIDDLEALIGN WD_STR_MIDDLEALIGN +#define WD_TEXT_BOTTOMALIGN WD_STR_BOTTOMALIGN +#define WD_TEXT_NOCLIP WD_STR_NOCLIP +#define WD_TEXT_NOWRAP WD_STR_NOWRAP +#define WD_TEXT_ENDELLIPSIS WD_STR_ENDELLIPSIS +#define WD_TEXT_WORDELLIPSIS WD_STR_WORDELLIPSIS +#define WD_TEXT_PATHELLIPSIS WD_STR_PATHELLIPSIS + +#define WD_TEXT_ALIGNMASK WD_STR_ALIGNMASK +#define WD_TEXT_VALIGNMASK WD_STR_VALIGNMASK +#define WD_TEXT_ELLIPSISMASK WD_STR_ELLIPSISMASK + + +WD_HTEXT wdCreateText(WD_HFONT hFont, const WD_RECT* pRect, + const WCHAR* pszText, int iTextLength, DWORD dwFlags); +void wdDestroyText(WD_HTEXT hText); + +/* Customizers of the text layout. + * + * Typically these apply some effects, other font or other text attribute + * to the specified range of the whole text. + */ + +#define WD_TEXTSTYLE_NORMAL 0 +#define WD_TEXTSTYLE_OBLIQUE 1 +#define WD_TEXTSTYLE_ITALIC 2 + +void wdSetTextFontSize(WD_HTEXT hText, UINT uPos, UINT uLen, float fSize); +void wdSetTextFontStyle(WD_HTEXT hText, UINT uPos, UINT uLen, int iStyle); +void wdSetTextFontWeight(WD_HTEXT hText, UINT uPos, UINT uLen, LONG lfWeight); +void wdSetTextStrikethrough(WD_HTEXT hText, UINT uPos, UINT uLen, BOOL bStrikethrough); +void wdSetTextUnderline(WD_HTEXT hText, UINT uPos, UINT uLen, BOOL bUnderline); + +/* Getters inspecting the text layout. + */ +typedef struct WD_TEXTMETRICS_tag WD_TEXTMETRICS; +struct WD_TEXTMETRICS_tag { + float fLeft; + float fTop; + float fWidth; /* Computed/required width. */ + float fWidthWithTrailingWhitespace; + float fHeight; /* Computed/required height. */ + float fInitialWidth; /* Width of the rect passed into wdCreateText() */ + float fInitialHeight; /* Height of the rect passed into wdCreateText() */ + UINT32 uMaxBidiDepth; + UINT32 uLineCount; +}; + +void wdTextMetrics(WD_HTEXT hText, WD_TEXTMETRICS* pMetrics); + +/* Draw the text. + */ +void wdDrawText(WD_HCANVAS hCanvas, WD_HTEXT hText, WD_HBRUSH hBrush, + float x, float y, DWORD dwFlags); + + #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c6f4c64..9dd0187 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -29,6 +29,7 @@ add_library(windrawlib STATIC path.c string.c strokestyle.c + text.c ) add_definitions(-DUNICODE -D_UNICODE) diff --git a/src/dummy/dwrite.h b/src/dummy/dwrite.h index 2008592..9b1ad33 100644 --- a/src/dummy/dwrite.h +++ b/src/dummy/dwrite.h @@ -157,6 +157,12 @@ struct dummy_DWRITE_TEXT_METRICS_tag { UINT32 lineCount; }; +typedef struct dummy_DWRITE_TEXT_RANGE_tag dummy_DWRITE_TEXT_RANGE; +struct dummy_DWRITE_TEXT_RANGE_tag { + UINT32 startPosition; + UINT32 length; +}; + /****************************** *** Forward declarations *** @@ -512,17 +518,17 @@ struct dummy_IDWriteTextLayoutVtbl_tag { STDMETHOD(dummy_SetMaxHeight)(void); STDMETHOD(dummy_SetFontCollection)(void); STDMETHOD(dummy_SetFontFamilyName)(void); - STDMETHOD(dummy_SetFontWeight)(void); - STDMETHOD(dummy_SetFontStyle)(void); + STDMETHOD(SetFontWeight)(dummy_IDWriteTextLayout*, dummy_DWRITE_FONT_WEIGHT, dummy_DWRITE_TEXT_RANGE); + STDMETHOD(SetFontStyle)(dummy_IDWriteTextLayout*, dummy_DWRITE_FONT_STYLE, dummy_DWRITE_TEXT_RANGE); STDMETHOD(dummy_SetFontStretch)(void); - STDMETHOD(dummy_SetFontSize)(void); - STDMETHOD(dummy_SetUnderline)(void); - STDMETHOD(dummy_SetStrikethrough)(void); + STDMETHOD(SetFontSize)(dummy_IDWriteTextLayout*, FLOAT, dummy_DWRITE_TEXT_RANGE); + STDMETHOD(SetUnderline)(dummy_IDWriteTextLayout*, BOOL, dummy_DWRITE_TEXT_RANGE); + STDMETHOD(SetStrikethrough)(dummy_IDWriteTextLayout*, BOOL, dummy_DWRITE_TEXT_RANGE); STDMETHOD(dummy_SetDrawingEffect)(void); STDMETHOD(dummy_SetInlineObject)(void); STDMETHOD(dummy_SetTypography)(void); STDMETHOD(dummy_SetLocaleName)(void); - STDMETHOD(dummy_GetMaxWidth)(void); + STDMETHOD_(FLOAT, GetMaxWidth)(dummy_IDWriteTextLayout*); STDMETHOD(dummy_GetMaxHeight)(void); STDMETHOD(dummy_GetFontCollection2)(void); STDMETHOD(dummy_GetFontFamilyNameLength2)(void); @@ -561,6 +567,12 @@ struct dummy_IDWriteTextLayout_tag { #define dummy_IDWriteTextLayout_SetWordWrapping(self,a) (self)->vtbl->SetWordWrapping(self,a) #define dummy_IDWriteTextLayout_SetReadingDirection(self,a) (self)->vtbl->SetReadingDirection(self,a) #define dummy_IDWriteTextLayout_SetTrimming(self,a,b) (self)->vtbl->SetTrimming(self,a,b) +#define dummy_IDWriteTextLayout_SetFontWeight(self,a,b) (self)->vtbl->SetFontWeight(self,a,b) +#define dummy_IDWriteTextLayout_SetFontStyle(self,a,b) (self)->vtbl->SetFontStyle(self,a,b) +#define dummy_IDWriteTextLayout_SetFontSize(self,a,b) (self)->vtbl->SetFontSize(self,a,b) +#define dummy_IDWriteTextLayout_SetStrikethrough(self,a,b) (self)->vtbl->SetStrikethrough(self,a,b) +#define dummy_IDWriteTextLayout_SetUnderline(self,a,b) (self)->vtbl->SetUnderline(self,a,b) +#define dummy_IDWriteTextLayout_GetMaxWidth(self) (self)->vtbl->GetMaxWidth(self) #define dummy_IDWriteTextLayout_GetMetrics(self,a) (self)->vtbl->GetMetrics(self,a) diff --git a/src/text.c b/src/text.c new file mode 100644 index 0000000..c6b26b3 --- /dev/null +++ b/src/text.c @@ -0,0 +1,163 @@ +/* + * WinDrawLib + * Copyright (c) 2019 Martin Mitas + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "misc.h" +#include "backend-d2d.h" +#include "backend-dwrite.h" +#include "backend-gdix.h" +#include "lock.h" + + +WD_HTEXT +wdCreateText(WD_HFONT hFont, const WD_RECT* pRect, + const WCHAR* pszText, int iTextLength, DWORD dwFlags) +{ + const WD_RECT dummyRect = { 0.0f, 0.0f, 0.0f, 0.0f }; + + if(pRect->x0 > pRect->x1 || pRect->y0 > pRect->y1) + pRect = &dummyRect; + + if(d2d_enabled()) { + dwrite_font_t* font = (dwrite_font_t*) hFont; + dummy_IDWriteTextLayout* layout; + + layout = dwrite_create_text_layout(font->tf, pRect, pszText, iTextLength, dwFlags); + + return (WD_HTEXT) layout; + } else { + WD_TRACE("wdCreateText: Not currently implemented for GDI+ back-end."); + return NULL; + } +} + +void +wdDestroyText(WD_HTEXT hText) +{ + if(d2d_enabled()) { + dummy_IDWriteTextLayout* layout = (dummy_IDWriteTextLayout*) hText; + dummy_IDWriteTextLayout_Release(layout); + } +} + +void +wdSetTextFontSize(WD_HTEXT hText, UINT uPos, UINT uLen, float fSize) +{ + if(d2d_enabled()) { + dummy_IDWriteTextLayout* layout = (dummy_IDWriteTextLayout*) hText; + dummy_DWRITE_TEXT_RANGE range = { uPos, uLen }; + + dummy_IDWriteTextLayout_SetFontSize(layout, fSize, range); + } +} + +void +wdSetTextFontStyle(WD_HTEXT hText, UINT uPos, UINT uLen, int iStyle) +{ + if(d2d_enabled()) { + dummy_IDWriteTextLayout* layout = (dummy_IDWriteTextLayout*) hText; + dummy_DWRITE_TEXT_RANGE range = { uPos, uLen }; + dummy_DWRITE_FONT_STYLE style; + + switch(iStyle) { + case WD_TEXTSTYLE_NORMAL: style = dummy_DWRITE_FONT_STYLE_NORMAL; break; + case WD_TEXTSTYLE_OBLIQUE: style = dummy_DWRITE_FONT_STYLE_OBLIQUE; break; + case WD_TEXTSTYLE_ITALIC: /* Pass through. */ + default: style = dummy_DWRITE_FONT_STYLE_ITALIC; break; + } + + dummy_IDWriteTextLayout_SetFontStyle(layout, style, range); + } +} + +void +wdSetTextFontWeight(WD_HTEXT hText, UINT uPos, UINT uLen, LONG lfWeight) +{ + if(d2d_enabled()) { + dummy_IDWriteTextLayout* layout = (dummy_IDWriteTextLayout*) hText; + dummy_DWRITE_TEXT_RANGE range = { uPos, uLen }; + + dummy_IDWriteTextLayout_SetFontWeight(layout, lfWeight, range); + } +} + +void +wdSetTextStrikethrough(WD_HTEXT hText, UINT uPos, UINT uLen, BOOL bStrikethrough) +{ + if(d2d_enabled()) { + dummy_IDWriteTextLayout* layout = (dummy_IDWriteTextLayout*) hText; + dummy_DWRITE_TEXT_RANGE range = { uPos, uLen }; + + dummy_IDWriteTextLayout_SetStrikethrough(layout, bStrikethrough, range); + } +} + +void +wdSetTextUnderline(WD_HTEXT hText, UINT uPos, UINT uLen, BOOL bUnderline) +{ + if(d2d_enabled()) { + dummy_IDWriteTextLayout* layout = (dummy_IDWriteTextLayout*) hText; + dummy_DWRITE_TEXT_RANGE range = { uPos, uLen }; + + dummy_IDWriteTextLayout_SetUnderline(layout, bUnderline, range); + } +} + +void +wdTextMetrics(WD_HTEXT hText, WD_TEXTMETRICS* pMetrics) +{ + if(d2d_enabled()) { + dummy_IDWriteTextLayout* layout = (dummy_IDWriteTextLayout*) hText; + + /* We have designed WD_TEXTMETRICS to be binary compatible with + * DWRITE_TEXT_METRICS. */ + dummy_IDWriteTextLayout_GetMetrics(layout, (dummy_DWRITE_TEXT_METRICS*) pMetrics); + } +} + +void +wdDrawText(WD_HCANVAS hCanvas, WD_HTEXT hText, WD_HBRUSH hBrush, + float x, float y, DWORD dwFlags) +{ + if(d2d_enabled()) { + dummy_D2D1_POINT_2F origin = { x, y }; + d2d_canvas_t* c = (d2d_canvas_t*) hCanvas; + dummy_ID2D1Brush* b = (dummy_ID2D1Brush*) hBrush; + dummy_IDWriteTextLayout* layout = (dummy_IDWriteTextLayout*) hText; + dummy_D2D1_MATRIX_3X2_F old_matrix; + + if(c->flags & D2D_CANVASFLAG_RTL) { + d2d_disable_rtl_transform(c, &old_matrix); + origin.x = (float)c->width - dummy_IDWriteTextLayout_GetMaxWidth(layout); + + dummy_IDWriteTextLayout_SetReadingDirection(layout, + dummy_DWRITE_READING_DIRECTION_RIGHT_TO_LEFT); + } + + dummy_ID2D1RenderTarget_DrawTextLayout(c->target, origin, layout, b, + (dwFlags & WD_STR_NOCLIP) ? 0 : dummy_D2D1_DRAW_TEXT_OPTIONS_CLIP); + + if(c->flags & D2D_CANVASFLAG_RTL) { + dummy_ID2D1RenderTarget_SetTransform(c->target, &old_matrix); + } + } +} From 41f6874d53f6f28c1a7ac8de5c98cbae957a4069 Mon Sep 17 00:00:00 2001 From: Martin Mitas Date: Sun, 17 Feb 2019 10:27:11 +0100 Subject: [PATCH 3/7] Add wdSetTextMaxWidth() and wdSetTextMaxHeight(). --- include/wdl.h | 2 ++ src/dummy/dwrite.h | 6 ++++-- src/text.c | 18 ++++++++++++++++++ 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/include/wdl.h b/include/wdl.h index c82e62e..fb42c90 100644 --- a/include/wdl.h +++ b/include/wdl.h @@ -688,6 +688,8 @@ void wdDestroyText(WD_HTEXT hText); #define WD_TEXTSTYLE_OBLIQUE 1 #define WD_TEXTSTYLE_ITALIC 2 +void wdSetTextMaxWidth(WD_HTEXT hText, float fWidth); +void wdSetTextMaxHeight(WD_HTEXT hText, float fHeight); void wdSetTextFontSize(WD_HTEXT hText, UINT uPos, UINT uLen, float fSize); void wdSetTextFontStyle(WD_HTEXT hText, UINT uPos, UINT uLen, int iStyle); void wdSetTextFontWeight(WD_HTEXT hText, UINT uPos, UINT uLen, LONG lfWeight); diff --git a/src/dummy/dwrite.h b/src/dummy/dwrite.h index 9b1ad33..a7b089a 100644 --- a/src/dummy/dwrite.h +++ b/src/dummy/dwrite.h @@ -514,8 +514,8 @@ struct dummy_IDWriteTextLayoutVtbl_tag { STDMETHOD(dummy_GetLocaleName)(void); /* IDWriteTextLayout methods */ - STDMETHOD(dummy_SetMaxWidth)(void); - STDMETHOD(dummy_SetMaxHeight)(void); + STDMETHOD(SetMaxWidth)(dummy_IDWriteTextLayout*, FLOAT); + STDMETHOD(SetMaxHeight)(dummy_IDWriteTextLayout*, FLOAT); STDMETHOD(dummy_SetFontCollection)(void); STDMETHOD(dummy_SetFontFamilyName)(void); STDMETHOD(SetFontWeight)(dummy_IDWriteTextLayout*, dummy_DWRITE_FONT_WEIGHT, dummy_DWRITE_TEXT_RANGE); @@ -567,6 +567,8 @@ struct dummy_IDWriteTextLayout_tag { #define dummy_IDWriteTextLayout_SetWordWrapping(self,a) (self)->vtbl->SetWordWrapping(self,a) #define dummy_IDWriteTextLayout_SetReadingDirection(self,a) (self)->vtbl->SetReadingDirection(self,a) #define dummy_IDWriteTextLayout_SetTrimming(self,a,b) (self)->vtbl->SetTrimming(self,a,b) +#define dummy_IDWriteTextLayout_SetMaxWidth(self,a) (self)->vtbl->SetMaxWidth(self,a) +#define dummy_IDWriteTextLayout_SetMaxHeight(self,a) (self)->vtbl->SetMaxHeight(self,a) #define dummy_IDWriteTextLayout_SetFontWeight(self,a,b) (self)->vtbl->SetFontWeight(self,a,b) #define dummy_IDWriteTextLayout_SetFontStyle(self,a,b) (self)->vtbl->SetFontStyle(self,a,b) #define dummy_IDWriteTextLayout_SetFontSize(self,a,b) (self)->vtbl->SetFontSize(self,a,b) diff --git a/src/text.c b/src/text.c index c6b26b3..92648df 100644 --- a/src/text.c +++ b/src/text.c @@ -59,6 +59,24 @@ wdDestroyText(WD_HTEXT hText) } } +void +wdSetTextMaxWidth(WD_HTEXT hText, float fWidth) +{ + if(d2d_enabled()) { + dummy_IDWriteTextLayout* layout = (dummy_IDWriteTextLayout*) hText; + dummy_IDWriteTextLayout_SetMaxWidth(layout, fWidth); + } +} + +void +wdSetTextMaxHeight(WD_HTEXT hText, float fHeight) +{ + if(d2d_enabled()) { + dummy_IDWriteTextLayout* layout = (dummy_IDWriteTextLayout*) hText; + dummy_IDWriteTextLayout_SetMaxHeight(layout, fHeight); + } +} + void wdSetTextFontSize(WD_HTEXT hText, UINT uPos, UINT uLen, float fSize) { From a8c133dca5816fc2119b362298bccfe16388bea6 Mon Sep 17 00:00:00 2001 From: Martin Mitas Date: Thu, 28 Feb 2019 19:27:26 +0100 Subject: [PATCH 4/7] examples/draw-cached.c: Improve the effect of caching. The cached canvas retains the painted contents; so we can skip all the painting (as long as we need to paint the stuff dfferently). --- examples/draw-cached.c | 85 ++++++++++++++++++++++++++++++------------ include/wdl.h | 6 +++ 2 files changed, 68 insertions(+), 23 deletions(-) diff --git a/examples/draw-cached.c b/examples/draw-cached.c index 0b360ab..5afebfc 100644 --- a/examples/draw-cached.c +++ b/examples/draw-cached.c @@ -6,6 +6,17 @@ static HWND hwndMain = NULL; + +/* The cached canvas (if not NULL). + * + * Note that if the caching is enabled, it remembers all its painted contents + * across all the WM_PAINT messages so as long as we do not need to paint it + * with different contents, we may just call wdBeginPaint() + wdEndPaint() to + * make it blit into the window. + * + * If only some sub-region needs to be painted differently, then only that + * part of it needs to be repainted. + */ static WD_HCANVAS hCachedCanvas = NULL; @@ -21,32 +32,44 @@ MainWinPaintToCanvas(WD_HCANVAS hCanvas, BOOL* pCanCache) int i; wdBeginPaint(hCanvas); - wdClear(hCanvas, WD_RGB(255,255,255)); - hBrush = wdCreateSolidBrush(hCanvas, 0); - - for(i = 0; i < 3; i++) { - float x = 10.0f + i * 20.0f; - float y = 10.0f + i * 20.0f; - wdSetSolidBrushColor(hBrush, fillColors[i]); - wdFillRect(hCanvas, hBrush, x, y, x + 100.0f, y + 100.0f); + /* This very simple example never changes what it paints; i.e. if we + * already cached the completely painted canvas, we may skip the paint + * code altogether. + * + * In real applications, we would have to paint only parts of the canvas + * which needs to be painted differently since the last time; e.g. to + * reflect a change in the application's state. + */ + if(hCanvas != hCachedCanvas) { + wdClear(hCanvas, WD_RGB(255,255,255)); + hBrush = wdCreateSolidBrush(hCanvas, 0); + + for(i = 0; i < 3; i++) { + float x = 10.0f + i * 20.0f; + float y = 10.0f + i * 20.0f; + + wdSetSolidBrushColor(hBrush, fillColors[i]); + wdFillRect(hCanvas, hBrush, x, y, x + 100.0f, y + 100.0f); + + wdSetSolidBrushColor(hBrush, drawColors[i]); + wdDrawRect(hCanvas, hBrush, x, y, x + 100.0f, y + 100.0f, 3.0f); + } - wdSetSolidBrushColor(hBrush, drawColors[i]); - wdDrawRect(hCanvas, hBrush, x, y, x + 100.0f, y + 100.0f, 3.0f); - } + for(i = 0; i < 3; i++) { + float x = 250.0f + i * 20.0f; + float y = 60.0f + i * 20.0f; - for(i = 0; i < 3; i++) { - float x = 250.0f + i * 20.0f; - float y = 60.0f + i * 20.0f; + wdSetSolidBrushColor(hBrush, fillColors[i]); + wdFillCircle(hCanvas, hBrush, x, y, 55.0f); - wdSetSolidBrushColor(hBrush, fillColors[i]); - wdFillCircle(hCanvas, hBrush, x, y, 55.0f); + wdSetSolidBrushColor(hBrush, drawColors[i]); + wdDrawCircle(hCanvas, hBrush, x, y, 55.0f, 3.0f); + } - wdSetSolidBrushColor(hBrush, drawColors[i]); - wdDrawCircle(hCanvas, hBrush, x, y, 55.0f, 3.0f); + wdDestroyBrush(hBrush); } - wdDestroyBrush(hBrush); *pCanCache = wdEndPaint(hCanvas); } @@ -68,6 +91,7 @@ MainWinPaint(void) MainWinPaintToCanvas(hCanvas, &bCanCache); + /* Cache the completely painted canvas if we are allowed to. */ if(bCanCache) { hCachedCanvas = hCanvas; } else { @@ -75,6 +99,7 @@ MainWinPaint(void) hCachedCanvas = NULL; } } + EndPaint(hwndMain, &ps); } /* Main window procedure */ @@ -87,14 +112,28 @@ MainWinProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) break; case WM_SIZE: - /* If we are caching the canvas for WM_PAINT, we need to resize it. */ if(wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED) { - if(hCachedCanvas != NULL) - wdResizeCanvas(hCachedCanvas, LOWORD(lParam), HIWORD(lParam)); + if(hCachedCanvas != NULL) { + /* We could call wdResizeCanvas() here. But that would + * loose the painted stuff on the canvas anyway, and save + * us "only" the reallocation of the canvas. + * + * For the sake of simplicity, we just destroy it all. */ + wdDestroyCanvas(hCachedCanvas); + hCachedCanvas = NULL; + } + InvalidateRect(hwndMain, NULL, FALSE); } return 0; + case WM_LBUTTONDOWN: + InvalidateRect(hwnd, NULL, FALSE); + return 0; + case WM_DISPLAYCHANGE: + /* Some relevant graphics settings has been changed and we cannot + * use the cached canvas as it can use incompatible pixel format, + * so discard it. */ if(hCachedCanvas != NULL) { wdDestroyCanvas(hCachedCanvas); hCachedCanvas = NULL; @@ -130,7 +169,7 @@ _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nC /* Create main window */ hwndMain = CreateWindow( _T("main_window"), _T("LibWinDraw Example"), - WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 550, 350, + WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 150, 350, NULL, NULL, hInstance, NULL ); SendMessage(hwndMain, WM_SETFONT, (WPARAM) GetStockObject(DEFAULT_GUI_FONT), diff --git a/include/wdl.h b/include/wdl.h index fb42c90..2130539 100644 --- a/include/wdl.h +++ b/include/wdl.h @@ -211,12 +211,18 @@ void wdDestroyCanvas(WD_HCANVAS hCanvas); * - The canvas has been created with wdCreateCanvasWithPaintStruct() and * is used strictly for handling WM_PAINT. * - wdEndPaint() returns TRUE. + * + * The cached canvas retains all the contents; so on the next WM_PAINT, + * the application can repaint only those arts of the canvas which need + * to present something new/different. */ void wdBeginPaint(WD_HCANVAS hCanvas); BOOL wdEndPaint(WD_HCANVAS hCanvas); /* This is supposed to be called to resize cached canvas (see above), if it * needs to be resized, typically as a response to WM_SIZE message. + * + * (Note however, that the painted contents of the canvas is lost.) */ BOOL wdResizeCanvas(WD_HCANVAS hCanvas, UINT uWidth, UINT uHeight); From 4d0d99bbba6e447ad2cc605a2e930d2f28650301 Mon Sep 17 00:00:00 2001 From: Martin Mitas Date: Sun, 17 Mar 2019 11:23:57 +0200 Subject: [PATCH 5/7] Add wdMinimalTextWidth(). --- include/wdl.h | 5 +++++ src/dummy/dwrite.h | 3 ++- src/text.c | 14 ++++++++++++++ 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/include/wdl.h b/include/wdl.h index 2130539..410719a 100644 --- a/include/wdl.h +++ b/include/wdl.h @@ -684,6 +684,11 @@ WD_HTEXT wdCreateText(WD_HFONT hFont, const WD_RECT* pRect, const WCHAR* pszText, int iTextLength, DWORD dwFlags); void wdDestroyText(WD_HTEXT hText); +/* Get minimal width required for reasonable output. If lower width is requested, + * there might be some line breaks inside words or similar unpleasant effects. + */ +float wdMinimalTextWidth(WD_HTEXT hText); + /* Customizers of the text layout. * * Typically these apply some effects, other font or other text attribute diff --git a/src/dummy/dwrite.h b/src/dummy/dwrite.h index a7b089a..7658c30 100644 --- a/src/dummy/dwrite.h +++ b/src/dummy/dwrite.h @@ -549,7 +549,7 @@ struct dummy_IDWriteTextLayoutVtbl_tag { STDMETHOD(GetMetrics)(dummy_IDWriteTextLayout*, dummy_DWRITE_TEXT_METRICS*); STDMETHOD(dummy_GetOverhangMetrics)(void); STDMETHOD(dummy_GetClusterMetrics)(void); - STDMETHOD(dummy_DetermineMinWidth)(void); + STDMETHOD(DetermineMinWidth)(dummy_IDWriteTextLayout*, FLOAT*); STDMETHOD(dummy_HitTestPoint)(void); STDMETHOD(dummy_HitTestTextPosition)(void); STDMETHOD(dummy_HitTestTextRange)(void); @@ -576,6 +576,7 @@ struct dummy_IDWriteTextLayout_tag { #define dummy_IDWriteTextLayout_SetUnderline(self,a,b) (self)->vtbl->SetUnderline(self,a,b) #define dummy_IDWriteTextLayout_GetMaxWidth(self) (self)->vtbl->GetMaxWidth(self) #define dummy_IDWriteTextLayout_GetMetrics(self,a) (self)->vtbl->GetMetrics(self,a) +#define dummy_IDWriteTextLayout_DetermineMinWidth(self,a) (self)->vtbl->DetermineMinWidth(self,a) #endif /* DUMMY_DWRITE_H */ diff --git a/src/text.c b/src/text.c index 92648df..758f3e5 100644 --- a/src/text.c +++ b/src/text.c @@ -59,6 +59,20 @@ wdDestroyText(WD_HTEXT hText) } } +float +wdMinimalTextWidth(WD_HTEXT hText) +{ + if(d2d_enabled()) { + dummy_IDWriteTextLayout* layout = (dummy_IDWriteTextLayout*) hText; + float min_width; + + dummy_IDWriteTextLayout_DetermineMinWidth(layout, &min_width); + return min_width; + } else { + return 0; + } +} + void wdSetTextMaxWidth(WD_HTEXT hText, float fWidth) { From 5eaca3c77e707a07826a243c1346369add934419 Mon Sep 17 00:00:00 2001 From: Martin Mitas Date: Mon, 18 Mar 2019 18:44:34 +0200 Subject: [PATCH 6/7] Add support for justified paragraphs. Note it is really supported only in DWrite backend on Windows 8 and newer. If not supported, it falls back to left alignement. --- include/wdl.h | 4 +++- src/backend-dwrite.c | 30 +++++++++++++++++------------- src/backend-gdix.c | 23 +++++++++++------------ src/dummy/dwrite.h | 3 ++- 4 files changed, 33 insertions(+), 27 deletions(-) diff --git a/include/wdl.h b/include/wdl.h index 410719a..ef92ed9 100644 --- a/include/wdl.h +++ b/include/wdl.h @@ -609,6 +609,7 @@ void wdBitBltHICON(WD_HCANVAS hCanvas, HICON hIcon, #define WD_STR_LEFTALIGN 0x0000 #define WD_STR_CENTERALIGN 0x0001 #define WD_STR_RIGHTALIGN 0x0002 +#define WD_STR_JUSTIFY 0x0003 /* Falls back to left align if not supported */ #define WD_STR_TOPALIGN 0x0000 #define WD_STR_MIDDLEALIGN 0x0004 #define WD_STR_BOTTOMALIGN 0x0008 @@ -618,7 +619,7 @@ void wdBitBltHICON(WD_HCANVAS hCanvas, HICON hIcon, #define WD_STR_WORDELLIPSIS 0x0080 #define WD_STR_PATHELLIPSIS 0x0100 -#define WD_STR_ALIGNMASK (WD_STR_LEFTALIGN | WD_STR_CENTERALIGN | WD_STR_RIGHTALIGN) +#define WD_STR_ALIGNMASK (WD_STR_LEFTALIGN | WD_STR_CENTERALIGN | WD_STR_RIGHTALIGN | WD_STR_JUSTIFY) #define WD_STR_VALIGNMASK (WD_STR_TOPALIGN | WD_STR_MIDDLEALIGN | WD_STR_BOTTOMALIGN) #define WD_STR_ELLIPSISMASK (WD_STR_ENDELLIPSIS | WD_STR_WORDELLIPSIS | WD_STR_PATHELLIPSIS) @@ -666,6 +667,7 @@ float wdStringHeight(WD_HFONT hFont, const WCHAR* pszText); #define WD_TEXT_LEFTALIGN WD_STR_LEFTALIGN #define WD_TEXT_CENTERALIGN WD_STR_CENTERALIGN #define WD_TEXT_RIGHTALIGN WD_STR_RIGHTALIGN +#define WD_TEXT_JUSTIFY WD_STR_JUSTIFY /* Falls back to left align if not supported */ #define WD_TEXT_TOPALIGN WD_STR_TOPALIGN #define WD_TEXT_MIDDLEALIGN WD_STR_MIDDLEALIGN #define WD_TEXT_BOTTOMALIGN WD_STR_BOTTOMALIGN diff --git a/src/backend-dwrite.c b/src/backend-dwrite.c index a9899dc..1549ea6 100644 --- a/src/backend-dwrite.c +++ b/src/backend-dwrite.c @@ -221,20 +221,24 @@ dwrite_create_text_layout(dummy_IDWriteTextFormat* tf, const WD_RECT* rect, return NULL; } - if(flags & WD_STR_RIGHTALIGN) - tla = dummy_DWRITE_TEXT_ALIGNMENT_TRAILING; - else if(flags & WD_STR_CENTERALIGN) - tla = dummy_DWRITE_TEXT_ALIGNMENT_CENTER; - else + switch(flags & WD_STR_ALIGNMASK) { + case WD_STR_LEFTALIGN: tla = dummy_DWRITE_TEXT_ALIGNMENT_LEADING; break; + case WD_STR_CENTERALIGN: tla = dummy_DWRITE_TEXT_ALIGNMENT_CENTER; break; + case WD_STR_RIGHTALIGN: tla = dummy_DWRITE_TEXT_ALIGNMENT_TRAILING; break; + case WD_STR_JUSTIFY: tla = dummy_DWRITE_TEXT_ALIGNMENT_JUSTIFY; break; + } + hr = dummy_IDWriteTextLayout_SetTextAlignment(layout, tla); + if(FAILED(hr) && tla == dummy_DWRITE_TEXT_ALIGNMENT_JUSTIFY) { + /* Older DWrite versions (Windows 7) do not support DWRITE_TEXT_ALIGNMENT_JUSTIFY. */ tla = dummy_DWRITE_TEXT_ALIGNMENT_LEADING; - dummy_IDWriteTextLayout_SetTextAlignment(layout, tla); - - if(flags & WD_STR_BOTTOMALIGN) - tla = dummy_DWRITE_PARAGRAPH_ALIGNMENT_FAR; - else if(flags & WD_STR_MIDDLEALIGN) - tla = dummy_DWRITE_PARAGRAPH_ALIGNMENT_CENTER; - else - tla = dummy_DWRITE_PARAGRAPH_ALIGNMENT_NEAR; + dummy_IDWriteTextLayout_SetTextAlignment(layout, tla); + } + + switch(flags & WD_STR_VALIGNMASK) { + case WD_STR_MIDDLEALIGN: tla = dummy_DWRITE_PARAGRAPH_ALIGNMENT_CENTER; break; + case WD_STR_BOTTOMALIGN: tla = dummy_DWRITE_PARAGRAPH_ALIGNMENT_FAR; break; + default: tla = dummy_DWRITE_PARAGRAPH_ALIGNMENT_NEAR; break; + } dummy_IDWriteTextLayout_SetParagraphAlignment(layout, tla); if(flags & WD_STR_NOWRAP) diff --git a/src/backend-gdix.c b/src/backend-gdix.c index ddda8ec..8adeccc 100644 --- a/src/backend-gdix.c +++ b/src/backend-gdix.c @@ -376,20 +376,19 @@ gdix_canvas_apply_string_flags(gdix_canvas_t* c, DWORD flags) int sff; int trim; - if(flags & WD_STR_RIGHTALIGN) - sfa = dummy_StringAlignmentFar; - else if(flags & WD_STR_CENTERALIGN) - sfa = dummy_StringAlignmentCenter; - else - sfa = dummy_StringAlignmentNear; + /* Note WD_STR_JUSTIFY is not supported here. */ + switch(flags & WD_STR_ALIGNMASK) { + case WD_STR_CENTERALIGN: sfa = dummy_StringAlignmentCenter; break; + case WD_STR_RIGHTALIGN: sfa = dummy_StringAlignmentFar; break; + default: sfa = dummy_StringAlignmentNear; break; + } gdix_vtable->fn_SetStringFormatAlign(c->string_format, sfa); - if(flags & WD_STR_BOTTOMALIGN) - sfa = dummy_StringAlignmentFar; - else if(flags & WD_STR_MIDDLEALIGN) - sfa = dummy_StringAlignmentCenter; - else - sfa = dummy_StringAlignmentNear; + switch(flags & WD_STR_VALIGNMASK) { + case WD_STR_MIDDLEALIGN: sfa = dummy_StringAlignmentCenter; break; + case WD_STR_BOTTOMALIGN: sfa = dummy_StringAlignmentFar; break; + default: sfa = dummy_StringAlignmentNear; break; + } gdix_vtable->fn_SetStringFormatLineAlign(c->string_format, sfa); sff = 0; diff --git a/src/dummy/dwrite.h b/src/dummy/dwrite.h index 7658c30..83b0a91 100644 --- a/src/dummy/dwrite.h +++ b/src/dummy/dwrite.h @@ -101,7 +101,8 @@ typedef enum dummy_DWRITE_TEXT_ALIGNMENT_tag dummy_DWRITE_TEXT_ALIGNMENT; enum dummy_DWRITE_TEXT_ALIGNMENT_tag { dummy_DWRITE_TEXT_ALIGNMENT_LEADING = 0, dummy_DWRITE_TEXT_ALIGNMENT_TRAILING, - dummy_DWRITE_TEXT_ALIGNMENT_CENTER + dummy_DWRITE_TEXT_ALIGNMENT_CENTER, + dummy_DWRITE_TEXT_ALIGNMENT_JUSTIFY }; typedef enum dummy_DWRITE_PARAGRAPH_ALIGNMENT_tag dummy_DWRITE_PARAGRAPH_ALIGNMENT; From 6e83886e23b3bc51bab45f2ae9cfae43bff1e693 Mon Sep 17 00:00:00 2001 From: Martin Mitas Date: Sat, 23 Mar 2019 14:57:39 +0200 Subject: [PATCH 7/7] Add wdSetTextFamily(). --- include/wdl.h | 1 + src/dummy/dwrite.h | 3 ++- src/text.c | 20 ++++++++++++++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/include/wdl.h b/include/wdl.h index ef92ed9..2a98daf 100644 --- a/include/wdl.h +++ b/include/wdl.h @@ -703,6 +703,7 @@ float wdMinimalTextWidth(WD_HTEXT hText); void wdSetTextMaxWidth(WD_HTEXT hText, float fWidth); void wdSetTextMaxHeight(WD_HTEXT hText, float fHeight); +BOOL wdSetTextFontFamily(WD_HTEXT hText, UINT uPos, UINT uLen, const WCHAR* pszFamilyName); void wdSetTextFontSize(WD_HTEXT hText, UINT uPos, UINT uLen, float fSize); void wdSetTextFontStyle(WD_HTEXT hText, UINT uPos, UINT uLen, int iStyle); void wdSetTextFontWeight(WD_HTEXT hText, UINT uPos, UINT uLen, LONG lfWeight); diff --git a/src/dummy/dwrite.h b/src/dummy/dwrite.h index 83b0a91..98a775f 100644 --- a/src/dummy/dwrite.h +++ b/src/dummy/dwrite.h @@ -518,7 +518,7 @@ struct dummy_IDWriteTextLayoutVtbl_tag { STDMETHOD(SetMaxWidth)(dummy_IDWriteTextLayout*, FLOAT); STDMETHOD(SetMaxHeight)(dummy_IDWriteTextLayout*, FLOAT); STDMETHOD(dummy_SetFontCollection)(void); - STDMETHOD(dummy_SetFontFamilyName)(void); + STDMETHOD(SetFontFamilyName)(dummy_IDWriteTextLayout*, const WCHAR*, dummy_DWRITE_TEXT_RANGE); STDMETHOD(SetFontWeight)(dummy_IDWriteTextLayout*, dummy_DWRITE_FONT_WEIGHT, dummy_DWRITE_TEXT_RANGE); STDMETHOD(SetFontStyle)(dummy_IDWriteTextLayout*, dummy_DWRITE_FONT_STYLE, dummy_DWRITE_TEXT_RANGE); STDMETHOD(dummy_SetFontStretch)(void); @@ -570,6 +570,7 @@ struct dummy_IDWriteTextLayout_tag { #define dummy_IDWriteTextLayout_SetTrimming(self,a,b) (self)->vtbl->SetTrimming(self,a,b) #define dummy_IDWriteTextLayout_SetMaxWidth(self,a) (self)->vtbl->SetMaxWidth(self,a) #define dummy_IDWriteTextLayout_SetMaxHeight(self,a) (self)->vtbl->SetMaxHeight(self,a) +#define dummy_IDWriteTextLayout_SetFontFamilyName(self,a,b) (self)->vtbl->SetFontFamilyName(self,a,b) #define dummy_IDWriteTextLayout_SetFontWeight(self,a,b) (self)->vtbl->SetFontWeight(self,a,b) #define dummy_IDWriteTextLayout_SetFontStyle(self,a,b) (self)->vtbl->SetFontStyle(self,a,b) #define dummy_IDWriteTextLayout_SetFontSize(self,a,b) (self)->vtbl->SetFontSize(self,a,b) diff --git a/src/text.c b/src/text.c index 758f3e5..3412772 100644 --- a/src/text.c +++ b/src/text.c @@ -91,6 +91,26 @@ wdSetTextMaxHeight(WD_HTEXT hText, float fHeight) } } +BOOL +wdSetTextFontFamily(WD_HTEXT hText, UINT uPos, UINT uLen, const WCHAR* pszFamilyName) +{ + if(d2d_enabled()) { + dummy_IDWriteTextLayout* layout = (dummy_IDWriteTextLayout*) hText; + dummy_DWRITE_TEXT_RANGE range = { uPos, uLen }; + HRESULT hr; + + hr = dummy_IDWriteTextLayout_SetFontFamilyName(layout, pszFamilyName, range); + if(FAILED(hr)) { + WD_TRACE_HR("wdSetTextFontFamily: IDWriteTextLayout::SetFontFamilyName() failed."); + return FALSE; + } + + return TRUE; + } else { + return FALSE; + } +} + void wdSetTextFontSize(WD_HTEXT hText, UINT uPos, UINT uLen, float fSize) {