xref: /MusicPlayer2/scintilla/win32/PlatWin.cxx (revision 8af74909132ed5e696cb05b6689ae4baf14c1c96)
1*8af74909SZhong Yang // Scintilla source code edit control
2*8af74909SZhong Yang /** @file PlatWin.cxx
3*8af74909SZhong Yang  ** Implementation of platform facilities on Windows.
4*8af74909SZhong Yang  **/
5*8af74909SZhong Yang // Copyright 1998-2003 by Neil Hodgson <[email protected]>
6*8af74909SZhong Yang // The License.txt file describes the conditions under which this software may be distributed.
7*8af74909SZhong Yang 
8*8af74909SZhong Yang #include <cstddef>
9*8af74909SZhong Yang #include <cstdlib>
10*8af74909SZhong Yang #include <cstring>
11*8af74909SZhong Yang #include <cstdio>
12*8af74909SZhong Yang #include <cstdarg>
13*8af74909SZhong Yang #include <ctime>
14*8af74909SZhong Yang #include <cmath>
15*8af74909SZhong Yang #include <climits>
16*8af74909SZhong Yang 
17*8af74909SZhong Yang #include <string_view>
18*8af74909SZhong Yang #include <vector>
19*8af74909SZhong Yang #include <map>
20*8af74909SZhong Yang #include <algorithm>
21*8af74909SZhong Yang #include <iterator>
22*8af74909SZhong Yang #include <memory>
23*8af74909SZhong Yang #include <mutex>
24*8af74909SZhong Yang 
25*8af74909SZhong Yang // Want to use std::min and std::max so don't want Windows.h version of min and max
26*8af74909SZhong Yang #if !defined(NOMINMAX)
27*8af74909SZhong Yang #define NOMINMAX
28*8af74909SZhong Yang #endif
29*8af74909SZhong Yang #undef _WIN32_WINNT
30*8af74909SZhong Yang #define _WIN32_WINNT 0x0500
31*8af74909SZhong Yang #undef WINVER
32*8af74909SZhong Yang #define WINVER 0x0500
33*8af74909SZhong Yang #include <windows.h>
34*8af74909SZhong Yang #include <commctrl.h>
35*8af74909SZhong Yang #include <richedit.h>
36*8af74909SZhong Yang #include <windowsx.h>
37*8af74909SZhong Yang 
38*8af74909SZhong Yang #if !defined(DISABLE_D2D)
39*8af74909SZhong Yang #define USE_D2D 1
40*8af74909SZhong Yang #endif
41*8af74909SZhong Yang 
42*8af74909SZhong Yang #if defined(USE_D2D)
43*8af74909SZhong Yang #include <d2d1.h>
44*8af74909SZhong Yang #include <dwrite.h>
45*8af74909SZhong Yang #endif
46*8af74909SZhong Yang 
47*8af74909SZhong Yang #include "Platform.h"
48*8af74909SZhong Yang #include "XPM.h"
49*8af74909SZhong Yang #include "UniConversion.h"
50*8af74909SZhong Yang #include "DBCS.h"
51*8af74909SZhong Yang #include "FontQuality.h"
52*8af74909SZhong Yang 
53*8af74909SZhong Yang #include "PlatWin.h"
54*8af74909SZhong Yang 
55*8af74909SZhong Yang #ifndef SPI_GETFONTSMOOTHINGCONTRAST
56*8af74909SZhong Yang #define SPI_GETFONTSMOOTHINGCONTRAST	0x200C
57*8af74909SZhong Yang #endif
58*8af74909SZhong Yang 
59*8af74909SZhong Yang #ifndef LOAD_LIBRARY_SEARCH_SYSTEM32
60*8af74909SZhong Yang #define LOAD_LIBRARY_SEARCH_SYSTEM32 0x00000800
61*8af74909SZhong Yang #endif
62*8af74909SZhong Yang 
63*8af74909SZhong Yang // __uuidof is a Microsoft extension but makes COM code neater, so disable warning
64*8af74909SZhong Yang #if defined(__clang__)
65*8af74909SZhong Yang #pragma clang diagnostic ignored "-Wlanguage-extension-token"
66*8af74909SZhong Yang #endif
67*8af74909SZhong Yang 
68*8af74909SZhong Yang namespace Scintilla {
69*8af74909SZhong Yang 
70*8af74909SZhong Yang UINT CodePageFromCharSet(DWORD characterSet, UINT documentCodePage) noexcept;
71*8af74909SZhong Yang 
72*8af74909SZhong Yang #if defined(USE_D2D)
73*8af74909SZhong Yang IDWriteFactory *pIDWriteFactory = nullptr;
74*8af74909SZhong Yang ID2D1Factory *pD2DFactory = nullptr;
75*8af74909SZhong Yang IDWriteRenderingParams *defaultRenderingParams = nullptr;
76*8af74909SZhong Yang IDWriteRenderingParams *customClearTypeRenderingParams = nullptr;
77*8af74909SZhong Yang D2D1_DRAW_TEXT_OPTIONS d2dDrawTextOptions = D2D1_DRAW_TEXT_OPTIONS_NONE;
78*8af74909SZhong Yang 
79*8af74909SZhong Yang static HMODULE hDLLD2D {};
80*8af74909SZhong Yang static HMODULE hDLLDWrite {};
81*8af74909SZhong Yang 
LoadD2DOnce()82*8af74909SZhong Yang void LoadD2DOnce() noexcept {
83*8af74909SZhong Yang 	DWORD loadLibraryFlags = 0;
84*8af74909SZhong Yang 	HMODULE kernel32 = ::GetModuleHandleW(L"kernel32.dll");
85*8af74909SZhong Yang 	if (kernel32) {
86*8af74909SZhong Yang 		if (::GetProcAddress(kernel32, "SetDefaultDllDirectories")) {
87*8af74909SZhong Yang 			// Availability of SetDefaultDllDirectories implies Windows 8+ or
88*8af74909SZhong Yang 			// that KB2533623 has been installed so LoadLibraryEx can be called
89*8af74909SZhong Yang 			// with LOAD_LIBRARY_SEARCH_SYSTEM32.
90*8af74909SZhong Yang 			loadLibraryFlags = LOAD_LIBRARY_SEARCH_SYSTEM32;
91*8af74909SZhong Yang 		}
92*8af74909SZhong Yang 	}
93*8af74909SZhong Yang 
94*8af74909SZhong Yang 	typedef HRESULT (WINAPI *D2D1CFSig)(D2D1_FACTORY_TYPE factoryType, REFIID riid,
95*8af74909SZhong Yang 		CONST D2D1_FACTORY_OPTIONS *pFactoryOptions, IUnknown **factory);
96*8af74909SZhong Yang 	typedef HRESULT (WINAPI *DWriteCFSig)(DWRITE_FACTORY_TYPE factoryType, REFIID iid,
97*8af74909SZhong Yang 		IUnknown **factory);
98*8af74909SZhong Yang 
99*8af74909SZhong Yang 	hDLLD2D = ::LoadLibraryEx(TEXT("D2D1.DLL"), 0, loadLibraryFlags);
100*8af74909SZhong Yang 	D2D1CFSig fnD2DCF = DLLFunction<D2D1CFSig>(hDLLD2D, "D2D1CreateFactory");
101*8af74909SZhong Yang 	if (fnD2DCF) {
102*8af74909SZhong Yang 		// A single threaded factory as Scintilla always draw on the GUI thread
103*8af74909SZhong Yang 		fnD2DCF(D2D1_FACTORY_TYPE_SINGLE_THREADED,
104*8af74909SZhong Yang 			__uuidof(ID2D1Factory),
105*8af74909SZhong Yang 			nullptr,
106*8af74909SZhong Yang 			reinterpret_cast<IUnknown**>(&pD2DFactory));
107*8af74909SZhong Yang 	}
108*8af74909SZhong Yang 	hDLLDWrite = ::LoadLibraryEx(TEXT("DWRITE.DLL"), 0, loadLibraryFlags);
109*8af74909SZhong Yang 	DWriteCFSig fnDWCF = DLLFunction<DWriteCFSig>(hDLLDWrite, "DWriteCreateFactory");
110*8af74909SZhong Yang 	if (fnDWCF) {
111*8af74909SZhong Yang 		const GUID IID_IDWriteFactory2 = // 0439fc60-ca44-4994-8dee-3a9af7b732ec
112*8af74909SZhong Yang 		{ 0x0439fc60, 0xca44, 0x4994, { 0x8d, 0xee, 0x3a, 0x9a, 0xf7, 0xb7, 0x32, 0xec } };
113*8af74909SZhong Yang 
114*8af74909SZhong Yang 		const HRESULT hr = fnDWCF(DWRITE_FACTORY_TYPE_SHARED,
115*8af74909SZhong Yang 			IID_IDWriteFactory2,
116*8af74909SZhong Yang 			reinterpret_cast<IUnknown**>(&pIDWriteFactory));
117*8af74909SZhong Yang 		if (SUCCEEDED(hr)) {
118*8af74909SZhong Yang 			// D2D1_DRAW_TEXT_OPTIONS_ENABLE_COLOR_FONT
119*8af74909SZhong Yang 			d2dDrawTextOptions = static_cast<D2D1_DRAW_TEXT_OPTIONS>(0x00000004);
120*8af74909SZhong Yang 		} else {
121*8af74909SZhong Yang 			fnDWCF(DWRITE_FACTORY_TYPE_SHARED,
122*8af74909SZhong Yang 				__uuidof(IDWriteFactory),
123*8af74909SZhong Yang 				reinterpret_cast<IUnknown**>(&pIDWriteFactory));
124*8af74909SZhong Yang 		}
125*8af74909SZhong Yang 	}
126*8af74909SZhong Yang 
127*8af74909SZhong Yang 	if (pIDWriteFactory) {
128*8af74909SZhong Yang 		const HRESULT hr = pIDWriteFactory->CreateRenderingParams(&defaultRenderingParams);
129*8af74909SZhong Yang 		if (SUCCEEDED(hr)) {
130*8af74909SZhong Yang 			unsigned int clearTypeContrast;
131*8af74909SZhong Yang 			if (::SystemParametersInfo(SPI_GETFONTSMOOTHINGCONTRAST, 0, &clearTypeContrast, 0)) {
132*8af74909SZhong Yang 
133*8af74909SZhong Yang 				FLOAT gamma;
134*8af74909SZhong Yang 				if (clearTypeContrast >= 1000 && clearTypeContrast <= 2200)
135*8af74909SZhong Yang 					gamma = static_cast<FLOAT>(clearTypeContrast) / 1000.0f;
136*8af74909SZhong Yang 				else
137*8af74909SZhong Yang 					gamma = defaultRenderingParams->GetGamma();
138*8af74909SZhong Yang 
139*8af74909SZhong Yang 				pIDWriteFactory->CreateCustomRenderingParams(gamma, defaultRenderingParams->GetEnhancedContrast(), defaultRenderingParams->GetClearTypeLevel(),
140*8af74909SZhong Yang 					defaultRenderingParams->GetPixelGeometry(), defaultRenderingParams->GetRenderingMode(), &customClearTypeRenderingParams);
141*8af74909SZhong Yang 			}
142*8af74909SZhong Yang 		}
143*8af74909SZhong Yang 	}
144*8af74909SZhong Yang }
145*8af74909SZhong Yang 
LoadD2D()146*8af74909SZhong Yang bool LoadD2D() {
147*8af74909SZhong Yang 	static std::once_flag once;
148*8af74909SZhong Yang 	std::call_once(once, LoadD2DOnce);
149*8af74909SZhong Yang 	return pIDWriteFactory && pD2DFactory;
150*8af74909SZhong Yang }
151*8af74909SZhong Yang 
152*8af74909SZhong Yang #endif
153*8af74909SZhong Yang 
154*8af74909SZhong Yang struct FormatAndMetrics {
155*8af74909SZhong Yang 	int technology;
156*8af74909SZhong Yang 	HFONT hfont;
157*8af74909SZhong Yang #if defined(USE_D2D)
158*8af74909SZhong Yang 	IDWriteTextFormat *pTextFormat;
159*8af74909SZhong Yang #endif
160*8af74909SZhong Yang 	int extraFontFlag;
161*8af74909SZhong Yang 	int characterSet;
162*8af74909SZhong Yang 	FLOAT yAscent;
163*8af74909SZhong Yang 	FLOAT yDescent;
164*8af74909SZhong Yang 	FLOAT yInternalLeading;
FormatAndMetricsScintilla::FormatAndMetrics165*8af74909SZhong Yang 	FormatAndMetrics(HFONT hfont_, int extraFontFlag_, int characterSet_) noexcept :
166*8af74909SZhong Yang 		technology(SCWIN_TECH_GDI), hfont(hfont_),
167*8af74909SZhong Yang #if defined(USE_D2D)
168*8af74909SZhong Yang 		pTextFormat(nullptr),
169*8af74909SZhong Yang #endif
170*8af74909SZhong Yang 		extraFontFlag(extraFontFlag_), characterSet(characterSet_), yAscent(2), yDescent(1), yInternalLeading(0) {
171*8af74909SZhong Yang 	}
172*8af74909SZhong Yang #if defined(USE_D2D)
FormatAndMetricsScintilla::FormatAndMetrics173*8af74909SZhong Yang 	FormatAndMetrics(IDWriteTextFormat *pTextFormat_,
174*8af74909SZhong Yang 	        int extraFontFlag_,
175*8af74909SZhong Yang 	        int characterSet_,
176*8af74909SZhong Yang 	        FLOAT yAscent_,
177*8af74909SZhong Yang 	        FLOAT yDescent_,
178*8af74909SZhong Yang 	        FLOAT yInternalLeading_) noexcept :
179*8af74909SZhong Yang 		technology(SCWIN_TECH_DIRECTWRITE),
180*8af74909SZhong Yang 		hfont{},
181*8af74909SZhong Yang 		pTextFormat(pTextFormat_),
182*8af74909SZhong Yang 		extraFontFlag(extraFontFlag_),
183*8af74909SZhong Yang 		characterSet(characterSet_),
184*8af74909SZhong Yang 		yAscent(yAscent_),
185*8af74909SZhong Yang 		yDescent(yDescent_),
186*8af74909SZhong Yang 		yInternalLeading(yInternalLeading_) {
187*8af74909SZhong Yang 	}
188*8af74909SZhong Yang #endif
189*8af74909SZhong Yang 	FormatAndMetrics(const FormatAndMetrics &) = delete;
190*8af74909SZhong Yang 	FormatAndMetrics(FormatAndMetrics &&) = delete;
191*8af74909SZhong Yang 	FormatAndMetrics &operator=(const FormatAndMetrics &) = delete;
192*8af74909SZhong Yang 	FormatAndMetrics &operator=(FormatAndMetrics &&) = delete;
193*8af74909SZhong Yang 
~FormatAndMetricsScintilla::FormatAndMetrics194*8af74909SZhong Yang 	~FormatAndMetrics() {
195*8af74909SZhong Yang 		if (hfont)
196*8af74909SZhong Yang 			::DeleteObject(hfont);
197*8af74909SZhong Yang #if defined(USE_D2D)
198*8af74909SZhong Yang 		ReleaseUnknown(pTextFormat);
199*8af74909SZhong Yang #endif
200*8af74909SZhong Yang 		extraFontFlag = 0;
201*8af74909SZhong Yang 		characterSet = 0;
202*8af74909SZhong Yang 		yAscent = 2;
203*8af74909SZhong Yang 		yDescent = 1;
204*8af74909SZhong Yang 		yInternalLeading = 0;
205*8af74909SZhong Yang 	}
206*8af74909SZhong Yang 	HFONT HFont() noexcept;
207*8af74909SZhong Yang };
208*8af74909SZhong Yang 
HFont()209*8af74909SZhong Yang HFONT FormatAndMetrics::HFont() noexcept {
210*8af74909SZhong Yang 	LOGFONTW lf = {};
211*8af74909SZhong Yang #if defined(USE_D2D)
212*8af74909SZhong Yang 	if (technology == SCWIN_TECH_GDI) {
213*8af74909SZhong Yang 		if (0 == ::GetObjectW(hfont, sizeof(lf), &lf)) {
214*8af74909SZhong Yang 			return {};
215*8af74909SZhong Yang 		}
216*8af74909SZhong Yang 	} else {
217*8af74909SZhong Yang 		const HRESULT hr = pTextFormat->GetFontFamilyName(lf.lfFaceName, LF_FACESIZE);
218*8af74909SZhong Yang 		if (!SUCCEEDED(hr)) {
219*8af74909SZhong Yang 			return {};
220*8af74909SZhong Yang 		}
221*8af74909SZhong Yang 		lf.lfWeight = pTextFormat->GetFontWeight();
222*8af74909SZhong Yang 		lf.lfItalic = pTextFormat->GetFontStyle() == DWRITE_FONT_STYLE_ITALIC;
223*8af74909SZhong Yang 		lf.lfHeight = -static_cast<int>(pTextFormat->GetFontSize());
224*8af74909SZhong Yang 	}
225*8af74909SZhong Yang #else
226*8af74909SZhong Yang 	if (0 == ::GetObjectW(hfont, sizeof(lf), &lf)) {
227*8af74909SZhong Yang 		return {};
228*8af74909SZhong Yang 	}
229*8af74909SZhong Yang #endif
230*8af74909SZhong Yang 	return ::CreateFontIndirectW(&lf);
231*8af74909SZhong Yang }
232*8af74909SZhong Yang 
233*8af74909SZhong Yang #ifndef CLEARTYPE_QUALITY
234*8af74909SZhong Yang #define CLEARTYPE_QUALITY 5
235*8af74909SZhong Yang #endif
236*8af74909SZhong Yang 
PointerFromWindow(HWND hWnd)237*8af74909SZhong Yang void *PointerFromWindow(HWND hWnd) noexcept {
238*8af74909SZhong Yang 	return reinterpret_cast<void *>(::GetWindowLongPtr(hWnd, 0));
239*8af74909SZhong Yang }
240*8af74909SZhong Yang 
SetWindowPointer(HWND hWnd,void * ptr)241*8af74909SZhong Yang void SetWindowPointer(HWND hWnd, void *ptr) noexcept {
242*8af74909SZhong Yang 	::SetWindowLongPtr(hWnd, 0, reinterpret_cast<LONG_PTR>(ptr));
243*8af74909SZhong Yang }
244*8af74909SZhong Yang 
245*8af74909SZhong Yang namespace {
246*8af74909SZhong Yang 
247*8af74909SZhong Yang // system DPI, same for all monitor.
248*8af74909SZhong Yang UINT uSystemDPI = USER_DEFAULT_SCREEN_DPI;
249*8af74909SZhong Yang 
250*8af74909SZhong Yang using GetDpiForWindowSig = UINT(WINAPI *)(HWND hwnd);
251*8af74909SZhong Yang GetDpiForWindowSig fnGetDpiForWindow = nullptr;
252*8af74909SZhong Yang 
253*8af74909SZhong Yang HMODULE hDLLShcore {};
254*8af74909SZhong Yang using GetDpiForMonitorSig = HRESULT (WINAPI *)(HMONITOR hmonitor, /*MONITOR_DPI_TYPE*/int dpiType, UINT *dpiX, UINT *dpiY);
255*8af74909SZhong Yang GetDpiForMonitorSig fnGetDpiForMonitor = nullptr;
256*8af74909SZhong Yang 
257*8af74909SZhong Yang using GetSystemMetricsForDpiSig = int(WINAPI *)(int nIndex, UINT dpi);
258*8af74909SZhong Yang GetSystemMetricsForDpiSig fnGetSystemMetricsForDpi = nullptr;
259*8af74909SZhong Yang 
260*8af74909SZhong Yang using AdjustWindowRectExForDpiSig = BOOL(WINAPI *)(LPRECT lpRect, DWORD dwStyle, BOOL bMenu, DWORD dwExStyle, UINT dpi);
261*8af74909SZhong Yang AdjustWindowRectExForDpiSig fnAdjustWindowRectExForDpi = nullptr;
262*8af74909SZhong Yang 
LoadDpiForWindow()263*8af74909SZhong Yang void LoadDpiForWindow() noexcept {
264*8af74909SZhong Yang 	HMODULE user32 = ::GetModuleHandleW(L"user32.dll");
265*8af74909SZhong Yang 	fnGetDpiForWindow = DLLFunction<GetDpiForWindowSig>(user32, "GetDpiForWindow");
266*8af74909SZhong Yang 	fnGetSystemMetricsForDpi = DLLFunction<GetSystemMetricsForDpiSig>(user32, "GetSystemMetricsForDpi");
267*8af74909SZhong Yang 	fnAdjustWindowRectExForDpi = DLLFunction<AdjustWindowRectExForDpiSig>(user32, "AdjustWindowRectExForDpi");
268*8af74909SZhong Yang 
269*8af74909SZhong Yang 	using GetDpiForSystemSig = UINT(WINAPI *)(void);
270*8af74909SZhong Yang 	GetDpiForSystemSig fnGetDpiForSystem = DLLFunction<GetDpiForSystemSig>(user32, "GetDpiForSystem");
271*8af74909SZhong Yang 	if (fnGetDpiForSystem) {
272*8af74909SZhong Yang 		uSystemDPI = fnGetDpiForSystem();
273*8af74909SZhong Yang 	} else {
274*8af74909SZhong Yang 		HDC hdcMeasure = ::CreateCompatibleDC({});
275*8af74909SZhong Yang 		uSystemDPI = ::GetDeviceCaps(hdcMeasure, LOGPIXELSY);
276*8af74909SZhong Yang 		::DeleteDC(hdcMeasure);
277*8af74909SZhong Yang 	}
278*8af74909SZhong Yang 
279*8af74909SZhong Yang 	if (!fnGetDpiForWindow) {
280*8af74909SZhong Yang 		hDLLShcore = ::LoadLibraryExW(L"shcore.dll", {}, LOAD_LIBRARY_SEARCH_SYSTEM32);
281*8af74909SZhong Yang 		if (hDLLShcore) {
282*8af74909SZhong Yang 			fnGetDpiForMonitor = DLLFunction<GetDpiForMonitorSig>(hDLLShcore, "GetDpiForMonitor");
283*8af74909SZhong Yang 		}
284*8af74909SZhong Yang 	}
285*8af74909SZhong Yang }
286*8af74909SZhong Yang 
287*8af74909SZhong Yang HINSTANCE hinstPlatformRes {};
288*8af74909SZhong Yang 
FamFromFontID(void * fid)289*8af74909SZhong Yang FormatAndMetrics *FamFromFontID(void *fid) noexcept {
290*8af74909SZhong Yang 	return static_cast<FormatAndMetrics *>(fid);
291*8af74909SZhong Yang }
292*8af74909SZhong Yang 
Win32MapFontQuality(int extraFontFlag)293*8af74909SZhong Yang constexpr BYTE Win32MapFontQuality(int extraFontFlag) noexcept {
294*8af74909SZhong Yang 	switch (extraFontFlag & SC_EFF_QUALITY_MASK) {
295*8af74909SZhong Yang 
296*8af74909SZhong Yang 		case SC_EFF_QUALITY_NON_ANTIALIASED:
297*8af74909SZhong Yang 			return NONANTIALIASED_QUALITY;
298*8af74909SZhong Yang 
299*8af74909SZhong Yang 		case SC_EFF_QUALITY_ANTIALIASED:
300*8af74909SZhong Yang 			return ANTIALIASED_QUALITY;
301*8af74909SZhong Yang 
302*8af74909SZhong Yang 		case SC_EFF_QUALITY_LCD_OPTIMIZED:
303*8af74909SZhong Yang 			return CLEARTYPE_QUALITY;
304*8af74909SZhong Yang 
305*8af74909SZhong Yang 		default:
306*8af74909SZhong Yang 			return SC_EFF_QUALITY_DEFAULT;
307*8af74909SZhong Yang 	}
308*8af74909SZhong Yang }
309*8af74909SZhong Yang 
310*8af74909SZhong Yang #if defined(USE_D2D)
DWriteMapFontQuality(int extraFontFlag)311*8af74909SZhong Yang constexpr D2D1_TEXT_ANTIALIAS_MODE DWriteMapFontQuality(int extraFontFlag) noexcept {
312*8af74909SZhong Yang 	switch (extraFontFlag & SC_EFF_QUALITY_MASK) {
313*8af74909SZhong Yang 
314*8af74909SZhong Yang 		case SC_EFF_QUALITY_NON_ANTIALIASED:
315*8af74909SZhong Yang 			return D2D1_TEXT_ANTIALIAS_MODE_ALIASED;
316*8af74909SZhong Yang 
317*8af74909SZhong Yang 		case SC_EFF_QUALITY_ANTIALIASED:
318*8af74909SZhong Yang 			return D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE;
319*8af74909SZhong Yang 
320*8af74909SZhong Yang 		case SC_EFF_QUALITY_LCD_OPTIMIZED:
321*8af74909SZhong Yang 			return D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE;
322*8af74909SZhong Yang 
323*8af74909SZhong Yang 		default:
324*8af74909SZhong Yang 			return D2D1_TEXT_ANTIALIAS_MODE_DEFAULT;
325*8af74909SZhong Yang 	}
326*8af74909SZhong Yang }
327*8af74909SZhong Yang #endif
328*8af74909SZhong Yang 
SetLogFont(LOGFONTW & lf,const char * faceName,int characterSet,float size,int weight,bool italic,int extraFontFlag)329*8af74909SZhong Yang void SetLogFont(LOGFONTW &lf, const char *faceName, int characterSet, float size, int weight, bool italic, int extraFontFlag) {
330*8af74909SZhong Yang 	lf = LOGFONTW();
331*8af74909SZhong Yang 	// The negative is to allow for leading
332*8af74909SZhong Yang 	lf.lfHeight = -(std::abs(std::lround(size)));
333*8af74909SZhong Yang 	lf.lfWeight = weight;
334*8af74909SZhong Yang 	lf.lfItalic = italic ? 1 : 0;
335*8af74909SZhong Yang 	lf.lfCharSet = static_cast<BYTE>(characterSet);
336*8af74909SZhong Yang 	lf.lfQuality = Win32MapFontQuality(extraFontFlag);
337*8af74909SZhong Yang 	UTF16FromUTF8(faceName, lf.lfFaceName, LF_FACESIZE);
338*8af74909SZhong Yang }
339*8af74909SZhong Yang 
CreateFontFromParameters(const FontParameters & fp)340*8af74909SZhong Yang FontID CreateFontFromParameters(const FontParameters &fp) {
341*8af74909SZhong Yang 	LOGFONTW lf;
342*8af74909SZhong Yang 	SetLogFont(lf, fp.faceName, fp.characterSet, fp.size, fp.weight, fp.italic, fp.extraFontFlag);
343*8af74909SZhong Yang 	FontID fid = nullptr;
344*8af74909SZhong Yang 	if (fp.technology == SCWIN_TECH_GDI) {
345*8af74909SZhong Yang 		HFONT hfont = ::CreateFontIndirectW(&lf);
346*8af74909SZhong Yang 		fid = new FormatAndMetrics(hfont, fp.extraFontFlag, fp.characterSet);
347*8af74909SZhong Yang 	} else {
348*8af74909SZhong Yang #if defined(USE_D2D)
349*8af74909SZhong Yang 		IDWriteTextFormat *pTextFormat = nullptr;
350*8af74909SZhong Yang 		const std::wstring wsFace = WStringFromUTF8(fp.faceName);
351*8af74909SZhong Yang 		const FLOAT fHeight = fp.size;
352*8af74909SZhong Yang 		const DWRITE_FONT_STYLE style = fp.italic ? DWRITE_FONT_STYLE_ITALIC : DWRITE_FONT_STYLE_NORMAL;
353*8af74909SZhong Yang 		HRESULT hr = pIDWriteFactory->CreateTextFormat(wsFace.c_str(), nullptr,
354*8af74909SZhong Yang 			static_cast<DWRITE_FONT_WEIGHT>(fp.weight),
355*8af74909SZhong Yang 			style,
356*8af74909SZhong Yang 			DWRITE_FONT_STRETCH_NORMAL, fHeight, L"en-us", &pTextFormat);
357*8af74909SZhong Yang 		if (SUCCEEDED(hr)) {
358*8af74909SZhong Yang 			pTextFormat->SetWordWrapping(DWRITE_WORD_WRAPPING_NO_WRAP);
359*8af74909SZhong Yang 
360*8af74909SZhong Yang 			FLOAT yAscent = 1.0f;
361*8af74909SZhong Yang 			FLOAT yDescent = 1.0f;
362*8af74909SZhong Yang 			FLOAT yInternalLeading = 0.0f;
363*8af74909SZhong Yang 			IDWriteTextLayout *pTextLayout = nullptr;
364*8af74909SZhong Yang 			hr = pIDWriteFactory->CreateTextLayout(L"X", 1, pTextFormat,
365*8af74909SZhong Yang 					100.0f, 100.0f, &pTextLayout);
366*8af74909SZhong Yang 			if (SUCCEEDED(hr) && pTextLayout) {
367*8af74909SZhong Yang 				constexpr int maxLines = 2;
368*8af74909SZhong Yang 				DWRITE_LINE_METRICS lineMetrics[maxLines]{};
369*8af74909SZhong Yang 				UINT32 lineCount = 0;
370*8af74909SZhong Yang 				hr = pTextLayout->GetLineMetrics(lineMetrics, maxLines, &lineCount);
371*8af74909SZhong Yang 				if (SUCCEEDED(hr)) {
372*8af74909SZhong Yang 					yAscent = lineMetrics[0].baseline;
373*8af74909SZhong Yang 					yDescent = lineMetrics[0].height - lineMetrics[0].baseline;
374*8af74909SZhong Yang 
375*8af74909SZhong Yang 					FLOAT emHeight;
376*8af74909SZhong Yang 					hr = pTextLayout->GetFontSize(0, &emHeight);
377*8af74909SZhong Yang 					if (SUCCEEDED(hr)) {
378*8af74909SZhong Yang 						yInternalLeading = lineMetrics[0].height - emHeight;
379*8af74909SZhong Yang 					}
380*8af74909SZhong Yang 				}
381*8af74909SZhong Yang 				ReleaseUnknown(pTextLayout);
382*8af74909SZhong Yang 				pTextFormat->SetLineSpacing(DWRITE_LINE_SPACING_METHOD_UNIFORM, lineMetrics[0].height, lineMetrics[0].baseline);
383*8af74909SZhong Yang 			}
384*8af74909SZhong Yang 			fid = new FormatAndMetrics(pTextFormat, fp.extraFontFlag, fp.characterSet, yAscent, yDescent, yInternalLeading);
385*8af74909SZhong Yang 		}
386*8af74909SZhong Yang #endif
387*8af74909SZhong Yang 	}
388*8af74909SZhong Yang 	return fid;
389*8af74909SZhong Yang }
390*8af74909SZhong Yang 
391*8af74909SZhong Yang }
392*8af74909SZhong Yang 
Font()393*8af74909SZhong Yang Font::Font() noexcept : fid{} {
394*8af74909SZhong Yang }
395*8af74909SZhong Yang 
~Font()396*8af74909SZhong Yang Font::~Font() {
397*8af74909SZhong Yang }
398*8af74909SZhong Yang 
Create(const FontParameters & fp)399*8af74909SZhong Yang void Font::Create(const FontParameters &fp) {
400*8af74909SZhong Yang 	Release();
401*8af74909SZhong Yang 	if (fp.faceName)
402*8af74909SZhong Yang 		fid = CreateFontFromParameters(fp);
403*8af74909SZhong Yang }
404*8af74909SZhong Yang 
Release()405*8af74909SZhong Yang void Font::Release() {
406*8af74909SZhong Yang 	if (fid)
407*8af74909SZhong Yang 		delete FamFromFontID(fid);
408*8af74909SZhong Yang 	fid = nullptr;
409*8af74909SZhong Yang }
410*8af74909SZhong Yang 
411*8af74909SZhong Yang // Buffer to hold strings and string position arrays without always allocating on heap.
412*8af74909SZhong Yang // May sometimes have string too long to allocate on stack. So use a fixed stack-allocated buffer
413*8af74909SZhong Yang // when less than safe size otherwise allocate on heap and free automatically.
414*8af74909SZhong Yang template<typename T, int lengthStandard>
415*8af74909SZhong Yang class VarBuffer {
416*8af74909SZhong Yang 	T bufferStandard[lengthStandard];
417*8af74909SZhong Yang public:
418*8af74909SZhong Yang 	T *buffer;
VarBuffer(size_t length)419*8af74909SZhong Yang 	explicit VarBuffer(size_t length) : buffer(nullptr) {
420*8af74909SZhong Yang 		if (length > lengthStandard) {
421*8af74909SZhong Yang 			buffer = new T[length];
422*8af74909SZhong Yang 		} else {
423*8af74909SZhong Yang 			buffer = bufferStandard;
424*8af74909SZhong Yang 		}
425*8af74909SZhong Yang 	}
426*8af74909SZhong Yang 	// Deleted so VarBuffer objects can not be copied.
427*8af74909SZhong Yang 	VarBuffer(const VarBuffer &) = delete;
428*8af74909SZhong Yang 	VarBuffer(VarBuffer &&) = delete;
429*8af74909SZhong Yang 	VarBuffer &operator=(const VarBuffer &) = delete;
430*8af74909SZhong Yang 	VarBuffer &operator=(VarBuffer &&) = delete;
431*8af74909SZhong Yang 
~VarBuffer()432*8af74909SZhong Yang 	~VarBuffer() {
433*8af74909SZhong Yang 		if (buffer != bufferStandard) {
434*8af74909SZhong Yang 			delete []buffer;
435*8af74909SZhong Yang 			buffer = nullptr;
436*8af74909SZhong Yang 		}
437*8af74909SZhong Yang 	}
438*8af74909SZhong Yang };
439*8af74909SZhong Yang 
440*8af74909SZhong Yang constexpr int stackBufferLength = 1000;
441*8af74909SZhong Yang class TextWide : public VarBuffer<wchar_t, stackBufferLength> {
442*8af74909SZhong Yang public:
443*8af74909SZhong Yang 	int tlen;	// Using int instead of size_t as most Win32 APIs take int.
TextWide(std::string_view text,bool unicodeMode,int codePage=0)444*8af74909SZhong Yang 	TextWide(std::string_view text, bool unicodeMode, int codePage=0) :
445*8af74909SZhong Yang 		VarBuffer<wchar_t, stackBufferLength>(text.length()) {
446*8af74909SZhong Yang 		if (unicodeMode) {
447*8af74909SZhong Yang 			tlen = static_cast<int>(UTF16FromUTF8(text, buffer, text.length()));
448*8af74909SZhong Yang 		} else {
449*8af74909SZhong Yang 			// Support Asian string display in 9x English
450*8af74909SZhong Yang 			tlen = ::MultiByteToWideChar(codePage, 0, text.data(), static_cast<int>(text.length()),
451*8af74909SZhong Yang 				buffer, static_cast<int>(text.length()));
452*8af74909SZhong Yang 		}
453*8af74909SZhong Yang 	}
454*8af74909SZhong Yang };
455*8af74909SZhong Yang typedef VarBuffer<XYPOSITION, stackBufferLength> TextPositions;
456*8af74909SZhong Yang 
DpiForWindow(WindowID wid)457*8af74909SZhong Yang UINT DpiForWindow(WindowID wid) noexcept {
458*8af74909SZhong Yang 	if (fnGetDpiForWindow) {
459*8af74909SZhong Yang 		return fnGetDpiForWindow(HwndFromWindowID(wid));
460*8af74909SZhong Yang 	}
461*8af74909SZhong Yang 	if (fnGetDpiForMonitor) {
462*8af74909SZhong Yang 		HMONITOR hMonitor = ::MonitorFromWindow(HwndFromWindowID(wid), MONITOR_DEFAULTTONEAREST);
463*8af74909SZhong Yang 		UINT dpiX = 0;
464*8af74909SZhong Yang 		UINT dpiY = 0;
465*8af74909SZhong Yang 		if (fnGetDpiForMonitor(hMonitor, 0 /*MDT_EFFECTIVE_DPI*/, &dpiX, &dpiY) == S_OK) {
466*8af74909SZhong Yang 			return dpiY;
467*8af74909SZhong Yang 		}
468*8af74909SZhong Yang 	}
469*8af74909SZhong Yang 	return uSystemDPI;
470*8af74909SZhong Yang }
471*8af74909SZhong Yang 
SystemMetricsForDpi(int nIndex,UINT dpi)472*8af74909SZhong Yang int SystemMetricsForDpi(int nIndex, UINT dpi) noexcept {
473*8af74909SZhong Yang 	if (fnGetSystemMetricsForDpi) {
474*8af74909SZhong Yang 		return fnGetSystemMetricsForDpi(nIndex, dpi);
475*8af74909SZhong Yang 	}
476*8af74909SZhong Yang 
477*8af74909SZhong Yang 	int value = ::GetSystemMetrics(nIndex);
478*8af74909SZhong Yang 	value = (dpi == uSystemDPI) ? value : ::MulDiv(value, dpi, uSystemDPI);
479*8af74909SZhong Yang 	return value;
480*8af74909SZhong Yang }
481*8af74909SZhong Yang 
482*8af74909SZhong Yang class SurfaceGDI : public Surface {
483*8af74909SZhong Yang 	bool unicodeMode=false;
484*8af74909SZhong Yang 	HDC hdc{};
485*8af74909SZhong Yang 	bool hdcOwned=false;
486*8af74909SZhong Yang 	HPEN pen{};
487*8af74909SZhong Yang 	HPEN penOld{};
488*8af74909SZhong Yang 	HBRUSH brush{};
489*8af74909SZhong Yang 	HBRUSH brushOld{};
490*8af74909SZhong Yang 	HFONT fontOld{};
491*8af74909SZhong Yang 	HBITMAP bitmap{};
492*8af74909SZhong Yang 	HBITMAP bitmapOld{};
493*8af74909SZhong Yang 
494*8af74909SZhong Yang 	int logPixelsY = USER_DEFAULT_SCREEN_DPI;
495*8af74909SZhong Yang 
496*8af74909SZhong Yang 	int maxWidthMeasure = INT_MAX;
497*8af74909SZhong Yang 	// There appears to be a 16 bit string length limit in GDI on NT.
498*8af74909SZhong Yang 	int maxLenText = 65535;
499*8af74909SZhong Yang 
500*8af74909SZhong Yang 	int codePage = 0;
501*8af74909SZhong Yang 
502*8af74909SZhong Yang 	void BrushColour(ColourDesired back) noexcept;
503*8af74909SZhong Yang 	void SetFont(const Font &font_) noexcept;
504*8af74909SZhong Yang 	void Clear() noexcept;
505*8af74909SZhong Yang 
506*8af74909SZhong Yang public:
507*8af74909SZhong Yang 	SurfaceGDI() noexcept;
508*8af74909SZhong Yang 	// Deleted so SurfaceGDI objects can not be copied.
509*8af74909SZhong Yang 	SurfaceGDI(const SurfaceGDI &) = delete;
510*8af74909SZhong Yang 	SurfaceGDI(SurfaceGDI &&) = delete;
511*8af74909SZhong Yang 	SurfaceGDI &operator=(const SurfaceGDI &) = delete;
512*8af74909SZhong Yang 	SurfaceGDI &operator=(SurfaceGDI &&) = delete;
513*8af74909SZhong Yang 
514*8af74909SZhong Yang 	~SurfaceGDI() noexcept override;
515*8af74909SZhong Yang 
516*8af74909SZhong Yang 	void Init(WindowID wid) override;
517*8af74909SZhong Yang 	void Init(SurfaceID sid, WindowID wid) override;
518*8af74909SZhong Yang 	void InitPixMap(int width, int height, Surface *surface_, WindowID wid) override;
519*8af74909SZhong Yang 
520*8af74909SZhong Yang 	void Release() override;
521*8af74909SZhong Yang 	bool Initialised() override;
522*8af74909SZhong Yang 	void PenColour(ColourDesired fore) override;
523*8af74909SZhong Yang 	int LogPixelsY() override;
524*8af74909SZhong Yang 	int DeviceHeightFont(int points) override;
525*8af74909SZhong Yang 	void MoveTo(int x_, int y_) override;
526*8af74909SZhong Yang 	void LineTo(int x_, int y_) override;
527*8af74909SZhong Yang 	void Polygon(Point *pts, size_t npts, ColourDesired fore, ColourDesired back) override;
528*8af74909SZhong Yang 	void RectangleDraw(PRectangle rc, ColourDesired fore, ColourDesired back) override;
529*8af74909SZhong Yang 	void FillRectangle(PRectangle rc, ColourDesired back) override;
530*8af74909SZhong Yang 	void FillRectangle(PRectangle rc, Surface &surfacePattern) override;
531*8af74909SZhong Yang 	void RoundedRectangle(PRectangle rc, ColourDesired fore, ColourDesired back) override;
532*8af74909SZhong Yang 	void AlphaRectangle(PRectangle rc, int cornerSize, ColourDesired fill, int alphaFill,
533*8af74909SZhong Yang 		ColourDesired outline, int alphaOutline, int flags) override;
534*8af74909SZhong Yang 	void GradientRectangle(PRectangle rc, const std::vector<ColourStop> &stops, GradientOptions options) override;
535*8af74909SZhong Yang 	void DrawRGBAImage(PRectangle rc, int width, int height, const unsigned char *pixelsImage) override;
536*8af74909SZhong Yang 	void Ellipse(PRectangle rc, ColourDesired fore, ColourDesired back) override;
537*8af74909SZhong Yang 	void Copy(PRectangle rc, Point from, Surface &surfaceSource) override;
538*8af74909SZhong Yang 
539*8af74909SZhong Yang 	std::unique_ptr<IScreenLineLayout> Layout(const IScreenLine *screenLine) override;
540*8af74909SZhong Yang 
541*8af74909SZhong Yang 	void DrawTextCommon(PRectangle rc, const Font &font_, XYPOSITION ybase, std::string_view text, UINT fuOptions);
542*8af74909SZhong Yang 	void DrawTextNoClip(PRectangle rc, Font &font_, XYPOSITION ybase, std::string_view text, ColourDesired fore, ColourDesired back) override;
543*8af74909SZhong Yang 	void DrawTextClipped(PRectangle rc, Font &font_, XYPOSITION ybase, std::string_view text, ColourDesired fore, ColourDesired back) override;
544*8af74909SZhong Yang 	void DrawTextTransparent(PRectangle rc, Font &font_, XYPOSITION ybase, std::string_view text, ColourDesired fore) override;
545*8af74909SZhong Yang 	void MeasureWidths(Font &font_, std::string_view text, XYPOSITION *positions) override;
546*8af74909SZhong Yang 	XYPOSITION WidthText(Font &font_, std::string_view text) override;
547*8af74909SZhong Yang 	XYPOSITION Ascent(Font &font_) override;
548*8af74909SZhong Yang 	XYPOSITION Descent(Font &font_) override;
549*8af74909SZhong Yang 	XYPOSITION InternalLeading(Font &font_) override;
550*8af74909SZhong Yang 	XYPOSITION Height(Font &font_) override;
551*8af74909SZhong Yang 	XYPOSITION AverageCharWidth(Font &font_) override;
552*8af74909SZhong Yang 
553*8af74909SZhong Yang 	void SetClip(PRectangle rc) override;
554*8af74909SZhong Yang 	void FlushCachedState() override;
555*8af74909SZhong Yang 
556*8af74909SZhong Yang 	void SetUnicodeMode(bool unicodeMode_) override;
557*8af74909SZhong Yang 	void SetDBCSMode(int codePage_) override;
558*8af74909SZhong Yang 	void SetBidiR2L(bool bidiR2L_) override;
559*8af74909SZhong Yang };
560*8af74909SZhong Yang 
SurfaceGDI()561*8af74909SZhong Yang SurfaceGDI::SurfaceGDI() noexcept {
562*8af74909SZhong Yang }
563*8af74909SZhong Yang 
~SurfaceGDI()564*8af74909SZhong Yang SurfaceGDI::~SurfaceGDI() noexcept {
565*8af74909SZhong Yang 	Clear();
566*8af74909SZhong Yang }
567*8af74909SZhong Yang 
Clear()568*8af74909SZhong Yang void SurfaceGDI::Clear() noexcept {
569*8af74909SZhong Yang 	if (penOld) {
570*8af74909SZhong Yang 		::SelectObject(hdc, penOld);
571*8af74909SZhong Yang 		::DeleteObject(pen);
572*8af74909SZhong Yang 		penOld = {};
573*8af74909SZhong Yang 	}
574*8af74909SZhong Yang 	pen = {};
575*8af74909SZhong Yang 	if (brushOld) {
576*8af74909SZhong Yang 		::SelectObject(hdc, brushOld);
577*8af74909SZhong Yang 		::DeleteObject(brush);
578*8af74909SZhong Yang 		brushOld = {};
579*8af74909SZhong Yang 	}
580*8af74909SZhong Yang 	brush = {};
581*8af74909SZhong Yang 	if (fontOld) {
582*8af74909SZhong Yang 		// Fonts are not deleted as they are owned by a Font object
583*8af74909SZhong Yang 		::SelectObject(hdc, fontOld);
584*8af74909SZhong Yang 		fontOld = {};
585*8af74909SZhong Yang 	}
586*8af74909SZhong Yang 	if (bitmapOld) {
587*8af74909SZhong Yang 		::SelectObject(hdc, bitmapOld);
588*8af74909SZhong Yang 		::DeleteObject(bitmap);
589*8af74909SZhong Yang 		bitmapOld = {};
590*8af74909SZhong Yang 	}
591*8af74909SZhong Yang 	bitmap = {};
592*8af74909SZhong Yang 	if (hdcOwned) {
593*8af74909SZhong Yang 		::DeleteDC(hdc);
594*8af74909SZhong Yang 		hdc = {};
595*8af74909SZhong Yang 		hdcOwned = false;
596*8af74909SZhong Yang 	}
597*8af74909SZhong Yang }
598*8af74909SZhong Yang 
Release()599*8af74909SZhong Yang void SurfaceGDI::Release() {
600*8af74909SZhong Yang 	Clear();
601*8af74909SZhong Yang }
602*8af74909SZhong Yang 
Initialised()603*8af74909SZhong Yang bool SurfaceGDI::Initialised() {
604*8af74909SZhong Yang 	return hdc != 0;
605*8af74909SZhong Yang }
606*8af74909SZhong Yang 
Init(WindowID wid)607*8af74909SZhong Yang void SurfaceGDI::Init(WindowID wid) {
608*8af74909SZhong Yang 	Release();
609*8af74909SZhong Yang 	hdc = ::CreateCompatibleDC({});
610*8af74909SZhong Yang 	hdcOwned = true;
611*8af74909SZhong Yang 	::SetTextAlign(hdc, TA_BASELINE);
612*8af74909SZhong Yang 	logPixelsY = DpiForWindow(wid);
613*8af74909SZhong Yang }
614*8af74909SZhong Yang 
Init(SurfaceID sid,WindowID wid)615*8af74909SZhong Yang void SurfaceGDI::Init(SurfaceID sid, WindowID wid) {
616*8af74909SZhong Yang 	Release();
617*8af74909SZhong Yang 	hdc = static_cast<HDC>(sid);
618*8af74909SZhong Yang 	::SetTextAlign(hdc, TA_BASELINE);
619*8af74909SZhong Yang 	// Windows on screen are scaled but printers are not.
620*8af74909SZhong Yang 	const bool printing = ::GetDeviceCaps(hdc, TECHNOLOGY) != DT_RASDISPLAY;
621*8af74909SZhong Yang 	logPixelsY = printing ? ::GetDeviceCaps(hdc, LOGPIXELSY) : DpiForWindow(wid);
622*8af74909SZhong Yang }
623*8af74909SZhong Yang 
InitPixMap(int width,int height,Surface * surface_,WindowID wid)624*8af74909SZhong Yang void SurfaceGDI::InitPixMap(int width, int height, Surface *surface_, WindowID wid) {
625*8af74909SZhong Yang 	Release();
626*8af74909SZhong Yang 	SurfaceGDI *psurfOther = dynamic_cast<SurfaceGDI *>(surface_);
627*8af74909SZhong Yang 	// Should only ever be called with a SurfaceGDI, not a SurfaceD2D
628*8af74909SZhong Yang 	PLATFORM_ASSERT(psurfOther);
629*8af74909SZhong Yang 	hdc = ::CreateCompatibleDC(psurfOther->hdc);
630*8af74909SZhong Yang 	hdcOwned = true;
631*8af74909SZhong Yang 	bitmap = ::CreateCompatibleBitmap(psurfOther->hdc, width, height);
632*8af74909SZhong Yang 	bitmapOld = SelectBitmap(hdc, bitmap);
633*8af74909SZhong Yang 	::SetTextAlign(hdc, TA_BASELINE);
634*8af74909SZhong Yang 	SetUnicodeMode(psurfOther->unicodeMode);
635*8af74909SZhong Yang 	SetDBCSMode(psurfOther->codePage);
636*8af74909SZhong Yang 	logPixelsY = DpiForWindow(wid);
637*8af74909SZhong Yang }
638*8af74909SZhong Yang 
PenColour(ColourDesired fore)639*8af74909SZhong Yang void SurfaceGDI::PenColour(ColourDesired fore) {
640*8af74909SZhong Yang 	if (pen) {
641*8af74909SZhong Yang 		::SelectObject(hdc, penOld);
642*8af74909SZhong Yang 		::DeleteObject(pen);
643*8af74909SZhong Yang 		pen = {};
644*8af74909SZhong Yang 		penOld = {};
645*8af74909SZhong Yang 	}
646*8af74909SZhong Yang 	pen = ::CreatePen(0,1,fore.AsInteger());
647*8af74909SZhong Yang 	penOld = SelectPen(hdc, pen);
648*8af74909SZhong Yang }
649*8af74909SZhong Yang 
BrushColour(ColourDesired back)650*8af74909SZhong Yang void SurfaceGDI::BrushColour(ColourDesired back) noexcept {
651*8af74909SZhong Yang 	if (brush) {
652*8af74909SZhong Yang 		::SelectObject(hdc, brushOld);
653*8af74909SZhong Yang 		::DeleteObject(brush);
654*8af74909SZhong Yang 		brush = {};
655*8af74909SZhong Yang 		brushOld = {};
656*8af74909SZhong Yang 	}
657*8af74909SZhong Yang 	brush = ::CreateSolidBrush(back.AsInteger());
658*8af74909SZhong Yang 	brushOld = SelectBrush(hdc, brush);
659*8af74909SZhong Yang }
660*8af74909SZhong Yang 
SetFont(const Font & font_)661*8af74909SZhong Yang void SurfaceGDI::SetFont(const Font &font_) noexcept {
662*8af74909SZhong Yang 	const FormatAndMetrics *pfm = FamFromFontID(font_.GetID());
663*8af74909SZhong Yang 	PLATFORM_ASSERT(pfm->technology == SCWIN_TECH_GDI);
664*8af74909SZhong Yang 	if (fontOld) {
665*8af74909SZhong Yang 		SelectFont(hdc, pfm->hfont);
666*8af74909SZhong Yang 	} else {
667*8af74909SZhong Yang 		fontOld = SelectFont(hdc, pfm->hfont);
668*8af74909SZhong Yang 	}
669*8af74909SZhong Yang }
670*8af74909SZhong Yang 
LogPixelsY()671*8af74909SZhong Yang int SurfaceGDI::LogPixelsY() {
672*8af74909SZhong Yang 	return logPixelsY;
673*8af74909SZhong Yang }
674*8af74909SZhong Yang 
DeviceHeightFont(int points)675*8af74909SZhong Yang int SurfaceGDI::DeviceHeightFont(int points) {
676*8af74909SZhong Yang 	return ::MulDiv(points, LogPixelsY(), 72);
677*8af74909SZhong Yang }
678*8af74909SZhong Yang 
MoveTo(int x_,int y_)679*8af74909SZhong Yang void SurfaceGDI::MoveTo(int x_, int y_) {
680*8af74909SZhong Yang 	::MoveToEx(hdc, x_, y_, nullptr);
681*8af74909SZhong Yang }
682*8af74909SZhong Yang 
LineTo(int x_,int y_)683*8af74909SZhong Yang void SurfaceGDI::LineTo(int x_, int y_) {
684*8af74909SZhong Yang 	::LineTo(hdc, x_, y_);
685*8af74909SZhong Yang }
686*8af74909SZhong Yang 
Polygon(Point * pts,size_t npts,ColourDesired fore,ColourDesired back)687*8af74909SZhong Yang void SurfaceGDI::Polygon(Point *pts, size_t npts, ColourDesired fore, ColourDesired back) {
688*8af74909SZhong Yang 	PenColour(fore);
689*8af74909SZhong Yang 	BrushColour(back);
690*8af74909SZhong Yang 	std::vector<POINT> outline;
691*8af74909SZhong Yang 	std::transform(pts, pts + npts, std::back_inserter(outline), POINTFromPoint);
692*8af74909SZhong Yang 	::Polygon(hdc, outline.data(), static_cast<int>(npts));
693*8af74909SZhong Yang }
694*8af74909SZhong Yang 
RectangleDraw(PRectangle rc,ColourDesired fore,ColourDesired back)695*8af74909SZhong Yang void SurfaceGDI::RectangleDraw(PRectangle rc, ColourDesired fore, ColourDesired back) {
696*8af74909SZhong Yang 	PenColour(back);
697*8af74909SZhong Yang 	BrushColour(fore);
698*8af74909SZhong Yang 	const RECT rcw = RectFromPRectangle(rc);
699*8af74909SZhong Yang 	::Rectangle(hdc, rcw.left, rcw.top, rcw.right, rcw.bottom);
700*8af74909SZhong Yang }
701*8af74909SZhong Yang 
FillRectangle(PRectangle rc,ColourDesired back)702*8af74909SZhong Yang void SurfaceGDI::FillRectangle(PRectangle rc, ColourDesired back) {
703*8af74909SZhong Yang 	// Using ExtTextOut rather than a FillRect ensures that no dithering occurs.
704*8af74909SZhong Yang 	// There is no need to allocate a brush either.
705*8af74909SZhong Yang 	const RECT rcw = RectFromPRectangle(rc);
706*8af74909SZhong Yang 	::SetBkColor(hdc, back.AsInteger());
707*8af74909SZhong Yang 	::ExtTextOut(hdc, rcw.left, rcw.top, ETO_OPAQUE, &rcw, TEXT(""), 0, nullptr);
708*8af74909SZhong Yang }
709*8af74909SZhong Yang 
FillRectangle(PRectangle rc,Surface & surfacePattern)710*8af74909SZhong Yang void SurfaceGDI::FillRectangle(PRectangle rc, Surface &surfacePattern) {
711*8af74909SZhong Yang 	HBRUSH br;
712*8af74909SZhong Yang 	if (SurfaceGDI *psgdi = dynamic_cast<SurfaceGDI *>(&surfacePattern); psgdi && psgdi->bitmap) {
713*8af74909SZhong Yang 		br = ::CreatePatternBrush(psgdi->bitmap);
714*8af74909SZhong Yang 	} else {	// Something is wrong so display in red
715*8af74909SZhong Yang 		br = ::CreateSolidBrush(RGB(0xff, 0, 0));
716*8af74909SZhong Yang 	}
717*8af74909SZhong Yang 	const RECT rcw = RectFromPRectangle(rc);
718*8af74909SZhong Yang 	::FillRect(hdc, &rcw, br);
719*8af74909SZhong Yang 	::DeleteObject(br);
720*8af74909SZhong Yang }
721*8af74909SZhong Yang 
RoundedRectangle(PRectangle rc,ColourDesired fore,ColourDesired back)722*8af74909SZhong Yang void SurfaceGDI::RoundedRectangle(PRectangle rc, ColourDesired fore, ColourDesired back) {
723*8af74909SZhong Yang 	PenColour(fore);
724*8af74909SZhong Yang 	BrushColour(back);
725*8af74909SZhong Yang 	const RECT rcw = RectFromPRectangle(rc);
726*8af74909SZhong Yang 	::RoundRect(hdc,
727*8af74909SZhong Yang 		rcw.left + 1, rcw.top,
728*8af74909SZhong Yang 		rcw.right - 1, rcw.bottom,
729*8af74909SZhong Yang 		8, 8);
730*8af74909SZhong Yang }
731*8af74909SZhong Yang 
732*8af74909SZhong Yang namespace {
733*8af74909SZhong Yang 
dwordFromBGRA(byte b,byte g,byte r,byte a)734*8af74909SZhong Yang constexpr DWORD dwordFromBGRA(byte b, byte g, byte r, byte a) noexcept {
735*8af74909SZhong Yang 	return (a << 24) | (r << 16) | (g << 8) | b;
736*8af74909SZhong Yang }
737*8af74909SZhong Yang 
AlphaScaled(unsigned char component,unsigned int alpha)738*8af74909SZhong Yang constexpr byte AlphaScaled(unsigned char component, unsigned int alpha) noexcept {
739*8af74909SZhong Yang 	return static_cast<byte>(component * alpha / 255);
740*8af74909SZhong Yang }
741*8af74909SZhong Yang 
dwordMultiplied(ColourDesired colour,unsigned int alpha)742*8af74909SZhong Yang constexpr DWORD dwordMultiplied(ColourDesired colour, unsigned int alpha) noexcept {
743*8af74909SZhong Yang 	return dwordFromBGRA(
744*8af74909SZhong Yang 		AlphaScaled(colour.GetBlue(), alpha),
745*8af74909SZhong Yang 		AlphaScaled(colour.GetGreen(), alpha),
746*8af74909SZhong Yang 		AlphaScaled(colour.GetRed(), alpha),
747*8af74909SZhong Yang 		static_cast<byte>(alpha));
748*8af74909SZhong Yang }
749*8af74909SZhong Yang 
750*8af74909SZhong Yang class DIBSection {
751*8af74909SZhong Yang 	HDC hMemDC {};
752*8af74909SZhong Yang 	HBITMAP hbmMem {};
753*8af74909SZhong Yang 	HBITMAP hbmOld {};
754*8af74909SZhong Yang 	SIZE size {};
755*8af74909SZhong Yang 	DWORD *pixels = nullptr;
756*8af74909SZhong Yang public:
757*8af74909SZhong Yang 	DIBSection(HDC hdc, SIZE size_) noexcept;
758*8af74909SZhong Yang 	// Deleted so DIBSection objects can not be copied.
759*8af74909SZhong Yang 	DIBSection(const DIBSection&) = delete;
760*8af74909SZhong Yang 	DIBSection(DIBSection&&) = delete;
761*8af74909SZhong Yang 	DIBSection &operator=(const DIBSection&) = delete;
762*8af74909SZhong Yang 	DIBSection &operator=(DIBSection&&) = delete;
763*8af74909SZhong Yang 	~DIBSection() noexcept;
operator bool() const764*8af74909SZhong Yang 	operator bool() const noexcept {
765*8af74909SZhong Yang 		return hMemDC && hbmMem && pixels;
766*8af74909SZhong Yang 	}
Pixels() const767*8af74909SZhong Yang 	DWORD *Pixels() const noexcept {
768*8af74909SZhong Yang 		return pixels;
769*8af74909SZhong Yang 	}
Bytes() const770*8af74909SZhong Yang 	unsigned char *Bytes() const noexcept {
771*8af74909SZhong Yang 		return reinterpret_cast<unsigned char *>(pixels);
772*8af74909SZhong Yang 	}
DC() const773*8af74909SZhong Yang 	HDC DC() const noexcept {
774*8af74909SZhong Yang 		return hMemDC;
775*8af74909SZhong Yang 	}
SetPixel(LONG x,LONG y,DWORD value)776*8af74909SZhong Yang 	void SetPixel(LONG x, LONG y, DWORD value) noexcept {
777*8af74909SZhong Yang 		PLATFORM_ASSERT(x >= 0);
778*8af74909SZhong Yang 		PLATFORM_ASSERT(y >= 0);
779*8af74909SZhong Yang 		PLATFORM_ASSERT(x < size.cx);
780*8af74909SZhong Yang 		PLATFORM_ASSERT(y < size.cy);
781*8af74909SZhong Yang 		pixels[y * size.cx + x] = value;
782*8af74909SZhong Yang 	}
783*8af74909SZhong Yang 	void SetSymmetric(LONG x, LONG y, DWORD value) noexcept;
784*8af74909SZhong Yang };
785*8af74909SZhong Yang 
DIBSection(HDC hdc,SIZE size_)786*8af74909SZhong Yang DIBSection::DIBSection(HDC hdc, SIZE size_) noexcept {
787*8af74909SZhong Yang 	hMemDC = ::CreateCompatibleDC(hdc);
788*8af74909SZhong Yang 	if (!hMemDC) {
789*8af74909SZhong Yang 		return;
790*8af74909SZhong Yang 	}
791*8af74909SZhong Yang 
792*8af74909SZhong Yang 	size = size_;
793*8af74909SZhong Yang 
794*8af74909SZhong Yang 	// -size.y makes bitmap start from top
795*8af74909SZhong Yang 	const BITMAPINFO bpih = { {sizeof(BITMAPINFOHEADER), size.cx, -size.cy, 1, 32, BI_RGB, 0, 0, 0, 0, 0},
796*8af74909SZhong Yang 		{{0, 0, 0, 0}} };
797*8af74909SZhong Yang 	void *image = nullptr;
798*8af74909SZhong Yang 	hbmMem = CreateDIBSection(hMemDC, &bpih, DIB_RGB_COLORS, &image, {}, 0);
799*8af74909SZhong Yang 	if (!hbmMem || !image) {
800*8af74909SZhong Yang 		return;
801*8af74909SZhong Yang 	}
802*8af74909SZhong Yang 	pixels = static_cast<DWORD *>(image);
803*8af74909SZhong Yang 	hbmOld = SelectBitmap(hMemDC, hbmMem);
804*8af74909SZhong Yang }
805*8af74909SZhong Yang 
~DIBSection()806*8af74909SZhong Yang DIBSection::~DIBSection() noexcept {
807*8af74909SZhong Yang 	if (hbmOld) {
808*8af74909SZhong Yang 		SelectBitmap(hMemDC, hbmOld);
809*8af74909SZhong Yang 		hbmOld = {};
810*8af74909SZhong Yang 	}
811*8af74909SZhong Yang 	if (hbmMem) {
812*8af74909SZhong Yang 		::DeleteObject(hbmMem);
813*8af74909SZhong Yang 		hbmMem = {};
814*8af74909SZhong Yang 	}
815*8af74909SZhong Yang 	if (hMemDC) {
816*8af74909SZhong Yang 		::DeleteDC(hMemDC);
817*8af74909SZhong Yang 		hMemDC = {};
818*8af74909SZhong Yang 	}
819*8af74909SZhong Yang }
820*8af74909SZhong Yang 
SetSymmetric(LONG x,LONG y,DWORD value)821*8af74909SZhong Yang void DIBSection::SetSymmetric(LONG x, LONG y, DWORD value) noexcept {
822*8af74909SZhong Yang 	// Plot a point symmetrically to all 4 quadrants
823*8af74909SZhong Yang 	const LONG xSymmetric = size.cx - 1 - x;
824*8af74909SZhong Yang 	const LONG ySymmetric = size.cy - 1 - y;
825*8af74909SZhong Yang 	SetPixel(x, y, value);
826*8af74909SZhong Yang 	SetPixel(xSymmetric, y, value);
827*8af74909SZhong Yang 	SetPixel(x, ySymmetric, value);
828*8af74909SZhong Yang 	SetPixel(xSymmetric, ySymmetric, value);
829*8af74909SZhong Yang }
830*8af74909SZhong Yang 
Proportional(unsigned char a,unsigned char b,float t)831*8af74909SZhong Yang constexpr unsigned int Proportional(unsigned char a, unsigned char b, float t) noexcept {
832*8af74909SZhong Yang 	return static_cast<unsigned int>(a + t * (b - a));
833*8af74909SZhong Yang }
834*8af74909SZhong Yang 
Proportional(ColourAlpha a,ColourAlpha b,float t)835*8af74909SZhong Yang ColourAlpha Proportional(ColourAlpha a, ColourAlpha b, float t) noexcept {
836*8af74909SZhong Yang 	return ColourAlpha(
837*8af74909SZhong Yang 		Proportional(a.GetRed(), b.GetRed(), t),
838*8af74909SZhong Yang 		Proportional(a.GetGreen(), b.GetGreen(), t),
839*8af74909SZhong Yang 		Proportional(a.GetBlue(), b.GetBlue(), t),
840*8af74909SZhong Yang 		Proportional(a.GetAlpha(), b.GetAlpha(), t));
841*8af74909SZhong Yang }
842*8af74909SZhong Yang 
GradientValue(const std::vector<ColourStop> & stops,float proportion)843*8af74909SZhong Yang ColourAlpha GradientValue(const std::vector<ColourStop> &stops, float proportion) noexcept {
844*8af74909SZhong Yang 	for (size_t stop = 0; stop < stops.size() - 1; stop++) {
845*8af74909SZhong Yang 		// Loop through each pair of stops
846*8af74909SZhong Yang 		const float positionStart = stops[stop].position;
847*8af74909SZhong Yang 		const float positionEnd = stops[stop + 1].position;
848*8af74909SZhong Yang 		if ((proportion >= positionStart) && (proportion <= positionEnd)) {
849*8af74909SZhong Yang 			const float proportionInPair = (proportion - positionStart) /
850*8af74909SZhong Yang 				(positionEnd - positionStart);
851*8af74909SZhong Yang 			return Proportional(stops[stop].colour, stops[stop + 1].colour, proportionInPair);
852*8af74909SZhong Yang 		}
853*8af74909SZhong Yang 	}
854*8af74909SZhong Yang 	// Loop should always find a value
855*8af74909SZhong Yang 	return ColourAlpha();
856*8af74909SZhong Yang }
857*8af74909SZhong Yang 
SizeOfRect(RECT rc)858*8af74909SZhong Yang constexpr SIZE SizeOfRect(RECT rc) noexcept {
859*8af74909SZhong Yang 	return { rc.right - rc.left, rc.bottom - rc.top };
860*8af74909SZhong Yang }
861*8af74909SZhong Yang 
862*8af74909SZhong Yang constexpr BLENDFUNCTION mergeAlpha = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA };
863*8af74909SZhong Yang 
864*8af74909SZhong Yang }
865*8af74909SZhong Yang 
AlphaRectangle(PRectangle rc,int cornerSize,ColourDesired fill,int alphaFill,ColourDesired outline,int alphaOutline,int)866*8af74909SZhong Yang void SurfaceGDI::AlphaRectangle(PRectangle rc, int cornerSize, ColourDesired fill, int alphaFill,
867*8af74909SZhong Yang 		ColourDesired outline, int alphaOutline, int /* flags*/ ) {
868*8af74909SZhong Yang 	const RECT rcw = RectFromPRectangle(rc);
869*8af74909SZhong Yang 	const SIZE size = SizeOfRect(rcw);
870*8af74909SZhong Yang 
871*8af74909SZhong Yang 	if (size.cx > 0) {
872*8af74909SZhong Yang 
873*8af74909SZhong Yang 		DIBSection section(hdc, size);
874*8af74909SZhong Yang 
875*8af74909SZhong Yang 		if (section) {
876*8af74909SZhong Yang 
877*8af74909SZhong Yang 			// Ensure not distorted too much by corners when small
878*8af74909SZhong Yang 			const LONG corner = std::min<LONG>(cornerSize, (std::min(size.cx, size.cy) / 2) - 2);
879*8af74909SZhong Yang 
880*8af74909SZhong Yang 			constexpr DWORD valEmpty = dwordFromBGRA(0,0,0,0);
881*8af74909SZhong Yang 			const DWORD valFill = dwordMultiplied(fill, alphaFill);
882*8af74909SZhong Yang 			const DWORD valOutline = dwordMultiplied(outline, alphaOutline);
883*8af74909SZhong Yang 
884*8af74909SZhong Yang 			// Draw a framed rectangle
885*8af74909SZhong Yang 			for (int y=0; y<size.cy; y++) {
886*8af74909SZhong Yang 				for (int x=0; x<size.cx; x++) {
887*8af74909SZhong Yang 					if ((x==0) || (x==size.cx-1) || (y == 0) || (y == size.cy -1)) {
888*8af74909SZhong Yang 						section.SetPixel(x, y, valOutline);
889*8af74909SZhong Yang 					} else {
890*8af74909SZhong Yang 						section.SetPixel(x, y, valFill);
891*8af74909SZhong Yang 					}
892*8af74909SZhong Yang 				}
893*8af74909SZhong Yang 			}
894*8af74909SZhong Yang 
895*8af74909SZhong Yang 			// Make the corners transparent
896*8af74909SZhong Yang 			for (LONG c=0; c<corner; c++) {
897*8af74909SZhong Yang 				for (LONG x=0; x<c+1; x++) {
898*8af74909SZhong Yang 					section.SetSymmetric(x, c - x, valEmpty);
899*8af74909SZhong Yang 				}
900*8af74909SZhong Yang 			}
901*8af74909SZhong Yang 
902*8af74909SZhong Yang 			// Draw the corner frame pieces
903*8af74909SZhong Yang 			for (LONG x=1; x<corner; x++) {
904*8af74909SZhong Yang 				section.SetSymmetric(x, corner - x, valOutline);
905*8af74909SZhong Yang 			}
906*8af74909SZhong Yang 
907*8af74909SZhong Yang 			AlphaBlend(hdc, rcw.left, rcw.top, size.cx, size.cy, section.DC(), 0, 0, size.cx, size.cy, mergeAlpha);
908*8af74909SZhong Yang 		}
909*8af74909SZhong Yang 	} else {
910*8af74909SZhong Yang 		BrushColour(outline);
911*8af74909SZhong Yang 		FrameRect(hdc, &rcw, brush);
912*8af74909SZhong Yang 	}
913*8af74909SZhong Yang }
914*8af74909SZhong Yang 
GradientRectangle(PRectangle rc,const std::vector<ColourStop> & stops,GradientOptions options)915*8af74909SZhong Yang void SurfaceGDI::GradientRectangle(PRectangle rc, const std::vector<ColourStop> &stops, GradientOptions options) {
916*8af74909SZhong Yang 
917*8af74909SZhong Yang 	const RECT rcw = RectFromPRectangle(rc);
918*8af74909SZhong Yang 	const SIZE size = SizeOfRect(rcw);
919*8af74909SZhong Yang 
920*8af74909SZhong Yang 	DIBSection section(hdc, size);
921*8af74909SZhong Yang 
922*8af74909SZhong Yang 	if (section) {
923*8af74909SZhong Yang 
924*8af74909SZhong Yang 		if (options == GradientOptions::topToBottom) {
925*8af74909SZhong Yang 			for (LONG y = 0; y < size.cy; y++) {
926*8af74909SZhong Yang 				// Find y/height proportional colour
927*8af74909SZhong Yang 				const float proportion = y / (rc.Height() - 1.0f);
928*8af74909SZhong Yang 				const ColourAlpha mixed = GradientValue(stops, proportion);
929*8af74909SZhong Yang 				const DWORD valFill = dwordMultiplied(mixed, mixed.GetAlpha());
930*8af74909SZhong Yang 				for (LONG x = 0; x < size.cx; x++) {
931*8af74909SZhong Yang 					section.SetPixel(x, y, valFill);
932*8af74909SZhong Yang 				}
933*8af74909SZhong Yang 			}
934*8af74909SZhong Yang 		} else {
935*8af74909SZhong Yang 			for (LONG x = 0; x < size.cx; x++) {
936*8af74909SZhong Yang 				// Find x/width proportional colour
937*8af74909SZhong Yang 				const float proportion = x / (rc.Width() - 1.0f);
938*8af74909SZhong Yang 				const ColourAlpha mixed = GradientValue(stops, proportion);
939*8af74909SZhong Yang 				const DWORD valFill = dwordMultiplied(mixed, mixed.GetAlpha());
940*8af74909SZhong Yang 				for (LONG y = 0; y < size.cy; y++) {
941*8af74909SZhong Yang 					section.SetPixel(x, y, valFill);
942*8af74909SZhong Yang 				}
943*8af74909SZhong Yang 			}
944*8af74909SZhong Yang 		}
945*8af74909SZhong Yang 
946*8af74909SZhong Yang 		AlphaBlend(hdc, rcw.left, rcw.top, size.cx, size.cy, section.DC(), 0, 0, size.cx, size.cy, mergeAlpha);
947*8af74909SZhong Yang 	}
948*8af74909SZhong Yang }
949*8af74909SZhong Yang 
DrawRGBAImage(PRectangle rc,int width,int height,const unsigned char * pixelsImage)950*8af74909SZhong Yang void SurfaceGDI::DrawRGBAImage(PRectangle rc, int width, int height, const unsigned char *pixelsImage) {
951*8af74909SZhong Yang 	if (rc.Width() > 0) {
952*8af74909SZhong Yang 		if (rc.Width() > width)
953*8af74909SZhong Yang 			rc.left += std::floor((rc.Width() - width) / 2);
954*8af74909SZhong Yang 		rc.right = rc.left + width;
955*8af74909SZhong Yang 		if (rc.Height() > height)
956*8af74909SZhong Yang 			rc.top += std::floor((rc.Height() - height) / 2);
957*8af74909SZhong Yang 		rc.bottom = rc.top + height;
958*8af74909SZhong Yang 
959*8af74909SZhong Yang 		const SIZE size { width, height };
960*8af74909SZhong Yang 		DIBSection section(hdc, size);
961*8af74909SZhong Yang 		if (section) {
962*8af74909SZhong Yang 			RGBAImage::BGRAFromRGBA(section.Bytes(), pixelsImage, width * height);
963*8af74909SZhong Yang 			AlphaBlend(hdc, static_cast<int>(rc.left), static_cast<int>(rc.top),
964*8af74909SZhong Yang 				static_cast<int>(rc.Width()), static_cast<int>(rc.Height()), section.DC(),
965*8af74909SZhong Yang 				0, 0, width, height, mergeAlpha);
966*8af74909SZhong Yang 		}
967*8af74909SZhong Yang 	}
968*8af74909SZhong Yang }
969*8af74909SZhong Yang 
Ellipse(PRectangle rc,ColourDesired fore,ColourDesired back)970*8af74909SZhong Yang void SurfaceGDI::Ellipse(PRectangle rc, ColourDesired fore, ColourDesired back) {
971*8af74909SZhong Yang 	PenColour(fore);
972*8af74909SZhong Yang 	BrushColour(back);
973*8af74909SZhong Yang 	const RECT rcw = RectFromPRectangle(rc);
974*8af74909SZhong Yang 	::Ellipse(hdc, rcw.left, rcw.top, rcw.right, rcw.bottom);
975*8af74909SZhong Yang }
976*8af74909SZhong Yang 
Copy(PRectangle rc,Point from,Surface & surfaceSource)977*8af74909SZhong Yang void SurfaceGDI::Copy(PRectangle rc, Point from, Surface &surfaceSource) {
978*8af74909SZhong Yang 	::BitBlt(hdc,
979*8af74909SZhong Yang 		static_cast<int>(rc.left), static_cast<int>(rc.top),
980*8af74909SZhong Yang 		static_cast<int>(rc.Width()), static_cast<int>(rc.Height()),
981*8af74909SZhong Yang 		dynamic_cast<SurfaceGDI &>(surfaceSource).hdc,
982*8af74909SZhong Yang 		static_cast<int>(from.x), static_cast<int>(from.y), SRCCOPY);
983*8af74909SZhong Yang }
984*8af74909SZhong Yang 
Layout(const IScreenLine *)985*8af74909SZhong Yang std::unique_ptr<IScreenLineLayout> SurfaceGDI::Layout(const IScreenLine *) {
986*8af74909SZhong Yang 	return {};
987*8af74909SZhong Yang }
988*8af74909SZhong Yang 
989*8af74909SZhong Yang typedef VarBuffer<int, stackBufferLength> TextPositionsI;
990*8af74909SZhong Yang 
DrawTextCommon(PRectangle rc,const Font & font_,XYPOSITION ybase,std::string_view text,UINT fuOptions)991*8af74909SZhong Yang void SurfaceGDI::DrawTextCommon(PRectangle rc, const Font &font_, XYPOSITION ybase, std::string_view text, UINT fuOptions) {
992*8af74909SZhong Yang 	SetFont(font_);
993*8af74909SZhong Yang 	const RECT rcw = RectFromPRectangle(rc);
994*8af74909SZhong Yang 	const int x = static_cast<int>(rc.left);
995*8af74909SZhong Yang 	const int yBaseInt = static_cast<int>(ybase);
996*8af74909SZhong Yang 
997*8af74909SZhong Yang 	if (unicodeMode) {
998*8af74909SZhong Yang 		const TextWide tbuf(text, unicodeMode, codePage);
999*8af74909SZhong Yang 		::ExtTextOutW(hdc, x, yBaseInt, fuOptions, &rcw, tbuf.buffer, tbuf.tlen, nullptr);
1000*8af74909SZhong Yang 	} else {
1001*8af74909SZhong Yang 		::ExtTextOutA(hdc, x, yBaseInt, fuOptions, &rcw, text.data(), static_cast<UINT>(text.length()), nullptr);
1002*8af74909SZhong Yang 	}
1003*8af74909SZhong Yang }
1004*8af74909SZhong Yang 
DrawTextNoClip(PRectangle rc,Font & font_,XYPOSITION ybase,std::string_view text,ColourDesired fore,ColourDesired back)1005*8af74909SZhong Yang void SurfaceGDI::DrawTextNoClip(PRectangle rc, Font &font_, XYPOSITION ybase, std::string_view text,
1006*8af74909SZhong Yang 	ColourDesired fore, ColourDesired back) {
1007*8af74909SZhong Yang 	::SetTextColor(hdc, fore.AsInteger());
1008*8af74909SZhong Yang 	::SetBkColor(hdc, back.AsInteger());
1009*8af74909SZhong Yang 	DrawTextCommon(rc, font_, ybase, text, ETO_OPAQUE);
1010*8af74909SZhong Yang }
1011*8af74909SZhong Yang 
DrawTextClipped(PRectangle rc,Font & font_,XYPOSITION ybase,std::string_view text,ColourDesired fore,ColourDesired back)1012*8af74909SZhong Yang void SurfaceGDI::DrawTextClipped(PRectangle rc, Font &font_, XYPOSITION ybase, std::string_view text,
1013*8af74909SZhong Yang 	ColourDesired fore, ColourDesired back) {
1014*8af74909SZhong Yang 	::SetTextColor(hdc, fore.AsInteger());
1015*8af74909SZhong Yang 	::SetBkColor(hdc, back.AsInteger());
1016*8af74909SZhong Yang 	DrawTextCommon(rc, font_, ybase, text, ETO_OPAQUE | ETO_CLIPPED);
1017*8af74909SZhong Yang }
1018*8af74909SZhong Yang 
DrawTextTransparent(PRectangle rc,Font & font_,XYPOSITION ybase,std::string_view text,ColourDesired fore)1019*8af74909SZhong Yang void SurfaceGDI::DrawTextTransparent(PRectangle rc, Font &font_, XYPOSITION ybase, std::string_view text,
1020*8af74909SZhong Yang 	ColourDesired fore) {
1021*8af74909SZhong Yang 	// Avoid drawing spaces in transparent mode
1022*8af74909SZhong Yang 	for (const char ch : text) {
1023*8af74909SZhong Yang 		if (ch != ' ') {
1024*8af74909SZhong Yang 			::SetTextColor(hdc, fore.AsInteger());
1025*8af74909SZhong Yang 			::SetBkMode(hdc, TRANSPARENT);
1026*8af74909SZhong Yang 			DrawTextCommon(rc, font_, ybase, text, 0);
1027*8af74909SZhong Yang 			::SetBkMode(hdc, OPAQUE);
1028*8af74909SZhong Yang 			return;
1029*8af74909SZhong Yang 		}
1030*8af74909SZhong Yang 	}
1031*8af74909SZhong Yang }
1032*8af74909SZhong Yang 
WidthText(Font & font_,std::string_view text)1033*8af74909SZhong Yang XYPOSITION SurfaceGDI::WidthText(Font &font_, std::string_view text) {
1034*8af74909SZhong Yang 	SetFont(font_);
1035*8af74909SZhong Yang 	SIZE sz={0,0};
1036*8af74909SZhong Yang 	if (!unicodeMode) {
1037*8af74909SZhong Yang 		::GetTextExtentPoint32A(hdc, text.data(), std::min(static_cast<int>(text.length()), maxLenText), &sz);
1038*8af74909SZhong Yang 	} else {
1039*8af74909SZhong Yang 		const TextWide tbuf(text, unicodeMode, codePage);
1040*8af74909SZhong Yang 		::GetTextExtentPoint32W(hdc, tbuf.buffer, tbuf.tlen, &sz);
1041*8af74909SZhong Yang 	}
1042*8af74909SZhong Yang 	return static_cast<XYPOSITION>(sz.cx);
1043*8af74909SZhong Yang }
1044*8af74909SZhong Yang 
MeasureWidths(Font & font_,std::string_view text,XYPOSITION * positions)1045*8af74909SZhong Yang void SurfaceGDI::MeasureWidths(Font &font_, std::string_view text, XYPOSITION *positions) {
1046*8af74909SZhong Yang 	// Zero positions to avoid random behaviour on failure.
1047*8af74909SZhong Yang 	std::fill(positions, positions + text.length(), 0.0f);
1048*8af74909SZhong Yang 	SetFont(font_);
1049*8af74909SZhong Yang 	SIZE sz={0,0};
1050*8af74909SZhong Yang 	int fit = 0;
1051*8af74909SZhong Yang 	int i = 0;
1052*8af74909SZhong Yang 	const int len = static_cast<int>(text.length());
1053*8af74909SZhong Yang 	if (unicodeMode) {
1054*8af74909SZhong Yang 		const TextWide tbuf(text, unicodeMode, codePage);
1055*8af74909SZhong Yang 		TextPositionsI poses(tbuf.tlen);
1056*8af74909SZhong Yang 		if (!::GetTextExtentExPointW(hdc, tbuf.buffer, tbuf.tlen, maxWidthMeasure, &fit, poses.buffer, &sz)) {
1057*8af74909SZhong Yang 			// Failure
1058*8af74909SZhong Yang 			return;
1059*8af74909SZhong Yang 		}
1060*8af74909SZhong Yang 		// Map the widths given for UTF-16 characters back onto the UTF-8 input string
1061*8af74909SZhong Yang 		for (int ui = 0; ui < fit; ui++) {
1062*8af74909SZhong Yang 			const unsigned char uch = text[i];
1063*8af74909SZhong Yang 			const unsigned int byteCount = UTF8BytesOfLead[uch];
1064*8af74909SZhong Yang 			if (byteCount == 4) {	// Non-BMP
1065*8af74909SZhong Yang 				ui++;
1066*8af74909SZhong Yang 			}
1067*8af74909SZhong Yang 			for (unsigned int bytePos=0; (bytePos<byteCount) && (i<len); bytePos++) {
1068*8af74909SZhong Yang 				positions[i++] = static_cast<XYPOSITION>(poses.buffer[ui]);
1069*8af74909SZhong Yang 			}
1070*8af74909SZhong Yang 		}
1071*8af74909SZhong Yang 	} else {
1072*8af74909SZhong Yang 		TextPositionsI poses(len);
1073*8af74909SZhong Yang 		if (!::GetTextExtentExPointA(hdc, text.data(), len, maxWidthMeasure, &fit, poses.buffer, &sz)) {
1074*8af74909SZhong Yang 			// Eeek - a NULL DC or other foolishness could cause this.
1075*8af74909SZhong Yang 			return;
1076*8af74909SZhong Yang 		}
1077*8af74909SZhong Yang 		while (i<fit) {
1078*8af74909SZhong Yang 			positions[i] = static_cast<XYPOSITION>(poses.buffer[i]);
1079*8af74909SZhong Yang 			i++;
1080*8af74909SZhong Yang 		}
1081*8af74909SZhong Yang 	}
1082*8af74909SZhong Yang 	// If any positions not filled in then use the last position for them
1083*8af74909SZhong Yang 	const XYPOSITION lastPos = (fit > 0) ? positions[fit - 1] : 0.0f;
1084*8af74909SZhong Yang 	std::fill(positions+i, positions + text.length(), lastPos);
1085*8af74909SZhong Yang }
1086*8af74909SZhong Yang 
Ascent(Font & font_)1087*8af74909SZhong Yang XYPOSITION SurfaceGDI::Ascent(Font &font_) {
1088*8af74909SZhong Yang 	SetFont(font_);
1089*8af74909SZhong Yang 	TEXTMETRIC tm;
1090*8af74909SZhong Yang 	::GetTextMetrics(hdc, &tm);
1091*8af74909SZhong Yang 	return static_cast<XYPOSITION>(tm.tmAscent);
1092*8af74909SZhong Yang }
1093*8af74909SZhong Yang 
Descent(Font & font_)1094*8af74909SZhong Yang XYPOSITION SurfaceGDI::Descent(Font &font_) {
1095*8af74909SZhong Yang 	SetFont(font_);
1096*8af74909SZhong Yang 	TEXTMETRIC tm;
1097*8af74909SZhong Yang 	::GetTextMetrics(hdc, &tm);
1098*8af74909SZhong Yang 	return static_cast<XYPOSITION>(tm.tmDescent);
1099*8af74909SZhong Yang }
1100*8af74909SZhong Yang 
InternalLeading(Font & font_)1101*8af74909SZhong Yang XYPOSITION SurfaceGDI::InternalLeading(Font &font_) {
1102*8af74909SZhong Yang 	SetFont(font_);
1103*8af74909SZhong Yang 	TEXTMETRIC tm;
1104*8af74909SZhong Yang 	::GetTextMetrics(hdc, &tm);
1105*8af74909SZhong Yang 	return static_cast<XYPOSITION>(tm.tmInternalLeading);
1106*8af74909SZhong Yang }
1107*8af74909SZhong Yang 
Height(Font & font_)1108*8af74909SZhong Yang XYPOSITION SurfaceGDI::Height(Font &font_) {
1109*8af74909SZhong Yang 	SetFont(font_);
1110*8af74909SZhong Yang 	TEXTMETRIC tm;
1111*8af74909SZhong Yang 	::GetTextMetrics(hdc, &tm);
1112*8af74909SZhong Yang 	return static_cast<XYPOSITION>(tm.tmHeight);
1113*8af74909SZhong Yang }
1114*8af74909SZhong Yang 
AverageCharWidth(Font & font_)1115*8af74909SZhong Yang XYPOSITION SurfaceGDI::AverageCharWidth(Font &font_) {
1116*8af74909SZhong Yang 	SetFont(font_);
1117*8af74909SZhong Yang 	TEXTMETRIC tm;
1118*8af74909SZhong Yang 	::GetTextMetrics(hdc, &tm);
1119*8af74909SZhong Yang 	return static_cast<XYPOSITION>(tm.tmAveCharWidth);
1120*8af74909SZhong Yang }
1121*8af74909SZhong Yang 
SetClip(PRectangle rc)1122*8af74909SZhong Yang void SurfaceGDI::SetClip(PRectangle rc) {
1123*8af74909SZhong Yang 	::IntersectClipRect(hdc, static_cast<int>(rc.left), static_cast<int>(rc.top),
1124*8af74909SZhong Yang 		static_cast<int>(rc.right), static_cast<int>(rc.bottom));
1125*8af74909SZhong Yang }
1126*8af74909SZhong Yang 
FlushCachedState()1127*8af74909SZhong Yang void SurfaceGDI::FlushCachedState() {
1128*8af74909SZhong Yang 	pen = {};
1129*8af74909SZhong Yang 	brush = {};
1130*8af74909SZhong Yang }
1131*8af74909SZhong Yang 
SetUnicodeMode(bool unicodeMode_)1132*8af74909SZhong Yang void SurfaceGDI::SetUnicodeMode(bool unicodeMode_) {
1133*8af74909SZhong Yang 	unicodeMode=unicodeMode_;
1134*8af74909SZhong Yang }
1135*8af74909SZhong Yang 
SetDBCSMode(int codePage_)1136*8af74909SZhong Yang void SurfaceGDI::SetDBCSMode(int codePage_) {
1137*8af74909SZhong Yang 	// No action on window as automatically handled by system.
1138*8af74909SZhong Yang 	codePage = codePage_;
1139*8af74909SZhong Yang }
1140*8af74909SZhong Yang 
SetBidiR2L(bool)1141*8af74909SZhong Yang void SurfaceGDI::SetBidiR2L(bool) {
1142*8af74909SZhong Yang }
1143*8af74909SZhong Yang 
1144*8af74909SZhong Yang #if defined(USE_D2D)
1145*8af74909SZhong Yang 
1146*8af74909SZhong Yang namespace {
1147*8af74909SZhong Yang 
RectangleFromPRectangle(PRectangle rc)1148*8af74909SZhong Yang constexpr D2D1_RECT_F RectangleFromPRectangle(PRectangle rc) noexcept {
1149*8af74909SZhong Yang 	return { rc.left, rc.top, rc.right, rc.bottom };
1150*8af74909SZhong Yang }
1151*8af74909SZhong Yang 
1152*8af74909SZhong Yang }
1153*8af74909SZhong Yang 
1154*8af74909SZhong Yang class BlobInline;
1155*8af74909SZhong Yang 
1156*8af74909SZhong Yang class SurfaceD2D : public Surface {
1157*8af74909SZhong Yang 	bool unicodeMode;
1158*8af74909SZhong Yang 	int x, y;
1159*8af74909SZhong Yang 
1160*8af74909SZhong Yang 	int codePage;
1161*8af74909SZhong Yang 	int codePageText;
1162*8af74909SZhong Yang 
1163*8af74909SZhong Yang 	ID2D1RenderTarget *pRenderTarget;
1164*8af74909SZhong Yang 	ID2D1BitmapRenderTarget *pBitmapRenderTarget;
1165*8af74909SZhong Yang 	bool ownRenderTarget;
1166*8af74909SZhong Yang 	int clipsActive;
1167*8af74909SZhong Yang 
1168*8af74909SZhong Yang 	IDWriteTextFormat *pTextFormat;
1169*8af74909SZhong Yang 	FLOAT yAscent;
1170*8af74909SZhong Yang 	FLOAT yDescent;
1171*8af74909SZhong Yang 	FLOAT yInternalLeading;
1172*8af74909SZhong Yang 
1173*8af74909SZhong Yang 	ID2D1SolidColorBrush *pBrush;
1174*8af74909SZhong Yang 
1175*8af74909SZhong Yang 	int logPixelsY;
1176*8af74909SZhong Yang 
1177*8af74909SZhong Yang 	void Clear() noexcept;
1178*8af74909SZhong Yang 	void SetFont(const Font &font_) noexcept;
1179*8af74909SZhong Yang 	HRESULT GetBitmap(ID2D1Bitmap **ppBitmap);
1180*8af74909SZhong Yang 
1181*8af74909SZhong Yang public:
1182*8af74909SZhong Yang 	SurfaceD2D() noexcept;
1183*8af74909SZhong Yang 	// Deleted so SurfaceD2D objects can not be copied.
1184*8af74909SZhong Yang 	SurfaceD2D(const SurfaceD2D &) = delete;
1185*8af74909SZhong Yang 	SurfaceD2D(SurfaceD2D &&) = delete;
1186*8af74909SZhong Yang 	SurfaceD2D &operator=(const SurfaceD2D &) = delete;
1187*8af74909SZhong Yang 	SurfaceD2D &operator=(SurfaceD2D &&) = delete;
1188*8af74909SZhong Yang 	~SurfaceD2D() override;
1189*8af74909SZhong Yang 
1190*8af74909SZhong Yang 	void SetScale(WindowID wid) noexcept;
1191*8af74909SZhong Yang 	void Init(WindowID wid) override;
1192*8af74909SZhong Yang 	void Init(SurfaceID sid, WindowID wid) override;
1193*8af74909SZhong Yang 	void InitPixMap(int width, int height, Surface *surface_, WindowID wid) override;
1194*8af74909SZhong Yang 
1195*8af74909SZhong Yang 	void Release() override;
1196*8af74909SZhong Yang 	bool Initialised() override;
1197*8af74909SZhong Yang 
1198*8af74909SZhong Yang 	HRESULT FlushDrawing();
1199*8af74909SZhong Yang 
1200*8af74909SZhong Yang 	void PenColour(ColourDesired fore) override;
1201*8af74909SZhong Yang 	void D2DPenColour(ColourDesired fore, int alpha=255);
1202*8af74909SZhong Yang 	int LogPixelsY() override;
1203*8af74909SZhong Yang 	int DeviceHeightFont(int points) override;
1204*8af74909SZhong Yang 	void MoveTo(int x_, int y_) override;
1205*8af74909SZhong Yang 	void LineTo(int x_, int y_) override;
1206*8af74909SZhong Yang 	void Polygon(Point *pts, size_t npts, ColourDesired fore, ColourDesired back) override;
1207*8af74909SZhong Yang 	void RectangleDraw(PRectangle rc, ColourDesired fore, ColourDesired back) override;
1208*8af74909SZhong Yang 	void FillRectangle(PRectangle rc, ColourDesired back) override;
1209*8af74909SZhong Yang 	void FillRectangle(PRectangle rc, Surface &surfacePattern) override;
1210*8af74909SZhong Yang 	void RoundedRectangle(PRectangle rc, ColourDesired fore, ColourDesired back) override;
1211*8af74909SZhong Yang 	void AlphaRectangle(PRectangle rc, int cornerSize, ColourDesired fill, int alphaFill,
1212*8af74909SZhong Yang 		ColourDesired outline, int alphaOutline, int flags) override;
1213*8af74909SZhong Yang 	void GradientRectangle(PRectangle rc, const std::vector<ColourStop> &stops, GradientOptions options) override;
1214*8af74909SZhong Yang 	void DrawRGBAImage(PRectangle rc, int width, int height, const unsigned char *pixelsImage) override;
1215*8af74909SZhong Yang 	void Ellipse(PRectangle rc, ColourDesired fore, ColourDesired back) override;
1216*8af74909SZhong Yang 	void Copy(PRectangle rc, Point from, Surface &surfaceSource) override;
1217*8af74909SZhong Yang 
1218*8af74909SZhong Yang 	std::unique_ptr<IScreenLineLayout> Layout(const IScreenLine *screenLine) override;
1219*8af74909SZhong Yang 
1220*8af74909SZhong Yang 	void DrawTextCommon(PRectangle rc, const Font &font_, XYPOSITION ybase, std::string_view text, UINT fuOptions);
1221*8af74909SZhong Yang 	void DrawTextNoClip(PRectangle rc, Font &font_, XYPOSITION ybase, std::string_view text, ColourDesired fore, ColourDesired back) override;
1222*8af74909SZhong Yang 	void DrawTextClipped(PRectangle rc, Font &font_, XYPOSITION ybase, std::string_view text, ColourDesired fore, ColourDesired back) override;
1223*8af74909SZhong Yang 	void DrawTextTransparent(PRectangle rc, Font &font_, XYPOSITION ybase, std::string_view text, ColourDesired fore) override;
1224*8af74909SZhong Yang 	void MeasureWidths(Font &font_, std::string_view text, XYPOSITION *positions) override;
1225*8af74909SZhong Yang 	XYPOSITION WidthText(Font &font_, std::string_view text) override;
1226*8af74909SZhong Yang 	XYPOSITION Ascent(Font &font_) override;
1227*8af74909SZhong Yang 	XYPOSITION Descent(Font &font_) override;
1228*8af74909SZhong Yang 	XYPOSITION InternalLeading(Font &font_) override;
1229*8af74909SZhong Yang 	XYPOSITION Height(Font &font_) override;
1230*8af74909SZhong Yang 	XYPOSITION AverageCharWidth(Font &font_) override;
1231*8af74909SZhong Yang 
1232*8af74909SZhong Yang 	void SetClip(PRectangle rc) override;
1233*8af74909SZhong Yang 	void FlushCachedState() override;
1234*8af74909SZhong Yang 
1235*8af74909SZhong Yang 	void SetUnicodeMode(bool unicodeMode_) override;
1236*8af74909SZhong Yang 	void SetDBCSMode(int codePage_) override;
1237*8af74909SZhong Yang 	void SetBidiR2L(bool bidiR2L_) override;
1238*8af74909SZhong Yang };
1239*8af74909SZhong Yang 
SurfaceD2D()1240*8af74909SZhong Yang SurfaceD2D::SurfaceD2D() noexcept :
1241*8af74909SZhong Yang 	unicodeMode(false),
1242*8af74909SZhong Yang 	x(0), y(0) {
1243*8af74909SZhong Yang 
1244*8af74909SZhong Yang 	codePage = 0;
1245*8af74909SZhong Yang 	codePageText = 0;
1246*8af74909SZhong Yang 
1247*8af74909SZhong Yang 	pRenderTarget = nullptr;
1248*8af74909SZhong Yang 	pBitmapRenderTarget = nullptr;
1249*8af74909SZhong Yang 	ownRenderTarget = false;
1250*8af74909SZhong Yang 	clipsActive = 0;
1251*8af74909SZhong Yang 
1252*8af74909SZhong Yang 	// From selected font
1253*8af74909SZhong Yang 	pTextFormat = nullptr;
1254*8af74909SZhong Yang 	yAscent = 2;
1255*8af74909SZhong Yang 	yDescent = 1;
1256*8af74909SZhong Yang 	yInternalLeading = 0;
1257*8af74909SZhong Yang 
1258*8af74909SZhong Yang 	pBrush = nullptr;
1259*8af74909SZhong Yang 
1260*8af74909SZhong Yang 	logPixelsY = USER_DEFAULT_SCREEN_DPI;
1261*8af74909SZhong Yang }
1262*8af74909SZhong Yang 
~SurfaceD2D()1263*8af74909SZhong Yang SurfaceD2D::~SurfaceD2D() {
1264*8af74909SZhong Yang 	Clear();
1265*8af74909SZhong Yang }
1266*8af74909SZhong Yang 
Clear()1267*8af74909SZhong Yang void SurfaceD2D::Clear() noexcept {
1268*8af74909SZhong Yang 	ReleaseUnknown(pBrush);
1269*8af74909SZhong Yang 	if (pRenderTarget) {
1270*8af74909SZhong Yang 		while (clipsActive) {
1271*8af74909SZhong Yang 			pRenderTarget->PopAxisAlignedClip();
1272*8af74909SZhong Yang 			clipsActive--;
1273*8af74909SZhong Yang 		}
1274*8af74909SZhong Yang 		if (ownRenderTarget) {
1275*8af74909SZhong Yang 			pRenderTarget->EndDraw();
1276*8af74909SZhong Yang 			ReleaseUnknown(pRenderTarget);
1277*8af74909SZhong Yang 			ownRenderTarget = false;
1278*8af74909SZhong Yang 		}
1279*8af74909SZhong Yang 		pRenderTarget = nullptr;
1280*8af74909SZhong Yang 	}
1281*8af74909SZhong Yang 	pBitmapRenderTarget = nullptr;
1282*8af74909SZhong Yang }
1283*8af74909SZhong Yang 
Release()1284*8af74909SZhong Yang void SurfaceD2D::Release() {
1285*8af74909SZhong Yang 	Clear();
1286*8af74909SZhong Yang }
1287*8af74909SZhong Yang 
SetScale(WindowID wid)1288*8af74909SZhong Yang void SurfaceD2D::SetScale(WindowID wid) noexcept {
1289*8af74909SZhong Yang 	logPixelsY = DpiForWindow(wid);
1290*8af74909SZhong Yang }
1291*8af74909SZhong Yang 
Initialised()1292*8af74909SZhong Yang bool SurfaceD2D::Initialised() {
1293*8af74909SZhong Yang 	return pRenderTarget != nullptr;
1294*8af74909SZhong Yang }
1295*8af74909SZhong Yang 
FlushDrawing()1296*8af74909SZhong Yang HRESULT SurfaceD2D::FlushDrawing() {
1297*8af74909SZhong Yang 	return pRenderTarget->Flush();
1298*8af74909SZhong Yang }
1299*8af74909SZhong Yang 
Init(WindowID wid)1300*8af74909SZhong Yang void SurfaceD2D::Init(WindowID wid) {
1301*8af74909SZhong Yang 	Release();
1302*8af74909SZhong Yang 	SetScale(wid);
1303*8af74909SZhong Yang }
1304*8af74909SZhong Yang 
Init(SurfaceID sid,WindowID wid)1305*8af74909SZhong Yang void SurfaceD2D::Init(SurfaceID sid, WindowID wid) {
1306*8af74909SZhong Yang 	Release();
1307*8af74909SZhong Yang 	SetScale(wid);
1308*8af74909SZhong Yang 	pRenderTarget = static_cast<ID2D1RenderTarget *>(sid);
1309*8af74909SZhong Yang }
1310*8af74909SZhong Yang 
InitPixMap(int width,int height,Surface * surface_,WindowID wid)1311*8af74909SZhong Yang void SurfaceD2D::InitPixMap(int width, int height, Surface *surface_, WindowID wid) {
1312*8af74909SZhong Yang 	Release();
1313*8af74909SZhong Yang 	SetScale(wid);
1314*8af74909SZhong Yang 	SurfaceD2D *psurfOther = dynamic_cast<SurfaceD2D *>(surface_);
1315*8af74909SZhong Yang 	// Should only ever be called with a SurfaceD2D, not a SurfaceGDI
1316*8af74909SZhong Yang 	PLATFORM_ASSERT(psurfOther);
1317*8af74909SZhong Yang 	const D2D1_SIZE_F desiredSize = D2D1::SizeF(static_cast<float>(width), static_cast<float>(height));
1318*8af74909SZhong Yang 	D2D1_PIXEL_FORMAT desiredFormat;
1319*8af74909SZhong Yang #ifdef __MINGW32__
1320*8af74909SZhong Yang 	desiredFormat.format = DXGI_FORMAT_UNKNOWN;
1321*8af74909SZhong Yang #else
1322*8af74909SZhong Yang 	desiredFormat = psurfOther->pRenderTarget->GetPixelFormat();
1323*8af74909SZhong Yang #endif
1324*8af74909SZhong Yang 	desiredFormat.alphaMode = D2D1_ALPHA_MODE_IGNORE;
1325*8af74909SZhong Yang 	const HRESULT hr = psurfOther->pRenderTarget->CreateCompatibleRenderTarget(
1326*8af74909SZhong Yang 		&desiredSize, nullptr, &desiredFormat, D2D1_COMPATIBLE_RENDER_TARGET_OPTIONS_NONE, &pBitmapRenderTarget);
1327*8af74909SZhong Yang 	if (SUCCEEDED(hr)) {
1328*8af74909SZhong Yang 		pRenderTarget = pBitmapRenderTarget;
1329*8af74909SZhong Yang 		pRenderTarget->BeginDraw();
1330*8af74909SZhong Yang 		ownRenderTarget = true;
1331*8af74909SZhong Yang 	}
1332*8af74909SZhong Yang 	SetUnicodeMode(psurfOther->unicodeMode);
1333*8af74909SZhong Yang 	SetDBCSMode(psurfOther->codePage);
1334*8af74909SZhong Yang }
1335*8af74909SZhong Yang 
GetBitmap(ID2D1Bitmap ** ppBitmap)1336*8af74909SZhong Yang HRESULT SurfaceD2D::GetBitmap(ID2D1Bitmap **ppBitmap) {
1337*8af74909SZhong Yang 	PLATFORM_ASSERT(pBitmapRenderTarget);
1338*8af74909SZhong Yang 	return pBitmapRenderTarget->GetBitmap(ppBitmap);
1339*8af74909SZhong Yang }
1340*8af74909SZhong Yang 
PenColour(ColourDesired fore)1341*8af74909SZhong Yang void SurfaceD2D::PenColour(ColourDesired fore) {
1342*8af74909SZhong Yang 	D2DPenColour(fore);
1343*8af74909SZhong Yang }
1344*8af74909SZhong Yang 
D2DPenColour(ColourDesired fore,int alpha)1345*8af74909SZhong Yang void SurfaceD2D::D2DPenColour(ColourDesired fore, int alpha) {
1346*8af74909SZhong Yang 	if (pRenderTarget) {
1347*8af74909SZhong Yang 		D2D_COLOR_F col;
1348*8af74909SZhong Yang 		col.r = fore.GetRedComponent();
1349*8af74909SZhong Yang 		col.g = fore.GetGreenComponent();
1350*8af74909SZhong Yang 		col.b = fore.GetBlueComponent();
1351*8af74909SZhong Yang 		col.a = alpha / 255.0f;
1352*8af74909SZhong Yang 		if (pBrush) {
1353*8af74909SZhong Yang 			pBrush->SetColor(col);
1354*8af74909SZhong Yang 		} else {
1355*8af74909SZhong Yang 			const HRESULT hr = pRenderTarget->CreateSolidColorBrush(col, &pBrush);
1356*8af74909SZhong Yang 			if (!SUCCEEDED(hr)) {
1357*8af74909SZhong Yang 				ReleaseUnknown(pBrush);
1358*8af74909SZhong Yang 			}
1359*8af74909SZhong Yang 		}
1360*8af74909SZhong Yang 	}
1361*8af74909SZhong Yang }
1362*8af74909SZhong Yang 
SetFont(const Font & font_)1363*8af74909SZhong Yang void SurfaceD2D::SetFont(const Font &font_) noexcept {
1364*8af74909SZhong Yang 	const FormatAndMetrics *pfm = FamFromFontID(font_.GetID());
1365*8af74909SZhong Yang 	PLATFORM_ASSERT(pfm->technology == SCWIN_TECH_DIRECTWRITE);
1366*8af74909SZhong Yang 	pTextFormat = pfm->pTextFormat;
1367*8af74909SZhong Yang 	yAscent = pfm->yAscent;
1368*8af74909SZhong Yang 	yDescent = pfm->yDescent;
1369*8af74909SZhong Yang 	yInternalLeading = pfm->yInternalLeading;
1370*8af74909SZhong Yang 	codePageText = codePage;
1371*8af74909SZhong Yang 	if (!unicodeMode && pfm->characterSet) {
1372*8af74909SZhong Yang 		codePageText = Scintilla::CodePageFromCharSet(pfm->characterSet, codePage);
1373*8af74909SZhong Yang 	}
1374*8af74909SZhong Yang 	if (pRenderTarget) {
1375*8af74909SZhong Yang 		D2D1_TEXT_ANTIALIAS_MODE aaMode;
1376*8af74909SZhong Yang 		aaMode = DWriteMapFontQuality(pfm->extraFontFlag);
1377*8af74909SZhong Yang 
1378*8af74909SZhong Yang 		if (aaMode == D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE && customClearTypeRenderingParams)
1379*8af74909SZhong Yang 			pRenderTarget->SetTextRenderingParams(customClearTypeRenderingParams);
1380*8af74909SZhong Yang 		else if (defaultRenderingParams)
1381*8af74909SZhong Yang 			pRenderTarget->SetTextRenderingParams(defaultRenderingParams);
1382*8af74909SZhong Yang 
1383*8af74909SZhong Yang 		pRenderTarget->SetTextAntialiasMode(aaMode);
1384*8af74909SZhong Yang 	}
1385*8af74909SZhong Yang }
1386*8af74909SZhong Yang 
LogPixelsY()1387*8af74909SZhong Yang int SurfaceD2D::LogPixelsY() {
1388*8af74909SZhong Yang 	return logPixelsY;
1389*8af74909SZhong Yang }
1390*8af74909SZhong Yang 
DeviceHeightFont(int points)1391*8af74909SZhong Yang int SurfaceD2D::DeviceHeightFont(int points) {
1392*8af74909SZhong Yang 	return ::MulDiv(points, LogPixelsY(), 72);
1393*8af74909SZhong Yang }
1394*8af74909SZhong Yang 
MoveTo(int x_,int y_)1395*8af74909SZhong Yang void SurfaceD2D::MoveTo(int x_, int y_) {
1396*8af74909SZhong Yang 	x = x_;
1397*8af74909SZhong Yang 	y = y_;
1398*8af74909SZhong Yang }
1399*8af74909SZhong Yang 
Delta(int difference)1400*8af74909SZhong Yang static constexpr int Delta(int difference) noexcept {
1401*8af74909SZhong Yang 	if (difference < 0)
1402*8af74909SZhong Yang 		return -1;
1403*8af74909SZhong Yang 	else if (difference > 0)
1404*8af74909SZhong Yang 		return 1;
1405*8af74909SZhong Yang 	else
1406*8af74909SZhong Yang 		return 0;
1407*8af74909SZhong Yang }
1408*8af74909SZhong Yang 
LineTo(int x_,int y_)1409*8af74909SZhong Yang void SurfaceD2D::LineTo(int x_, int y_) {
1410*8af74909SZhong Yang 	if (pRenderTarget) {
1411*8af74909SZhong Yang 		const int xDiff = x_ - x;
1412*8af74909SZhong Yang 		const int xDelta = Delta(xDiff);
1413*8af74909SZhong Yang 		const int yDiff = y_ - y;
1414*8af74909SZhong Yang 		const int yDelta = Delta(yDiff);
1415*8af74909SZhong Yang 		if ((xDiff == 0) || (yDiff == 0)) {
1416*8af74909SZhong Yang 			// Horizontal or vertical lines can be more precisely drawn as a filled rectangle
1417*8af74909SZhong Yang 			const int xEnd = x_ - xDelta;
1418*8af74909SZhong Yang 			const int left = std::min(x, xEnd);
1419*8af74909SZhong Yang 			const int width = std::abs(x - xEnd) + 1;
1420*8af74909SZhong Yang 			const int yEnd = y_ - yDelta;
1421*8af74909SZhong Yang 			const int top = std::min(y, yEnd);
1422*8af74909SZhong Yang 			const int height = std::abs(y - yEnd) + 1;
1423*8af74909SZhong Yang 			const D2D1_RECT_F rectangle1 = D2D1::RectF(static_cast<float>(left), static_cast<float>(top),
1424*8af74909SZhong Yang 				static_cast<float>(left+width), static_cast<float>(top+height));
1425*8af74909SZhong Yang 			pRenderTarget->FillRectangle(&rectangle1, pBrush);
1426*8af74909SZhong Yang 		} else if ((std::abs(xDiff) == std::abs(yDiff))) {
1427*8af74909SZhong Yang 			// 45 degree slope
1428*8af74909SZhong Yang 			pRenderTarget->DrawLine(D2D1::Point2F(x + 0.5f, y + 0.5f),
1429*8af74909SZhong Yang 				D2D1::Point2F(x_ + 0.5f - xDelta, y_ + 0.5f - yDelta), pBrush);
1430*8af74909SZhong Yang 		} else {
1431*8af74909SZhong Yang 			// Line has a different slope so difficult to avoid last pixel
1432*8af74909SZhong Yang 			pRenderTarget->DrawLine(D2D1::Point2F(x + 0.5f, y + 0.5f),
1433*8af74909SZhong Yang 				D2D1::Point2F(x_ + 0.5f, y_ + 0.5f), pBrush);
1434*8af74909SZhong Yang 		}
1435*8af74909SZhong Yang 		x = x_;
1436*8af74909SZhong Yang 		y = y_;
1437*8af74909SZhong Yang 	}
1438*8af74909SZhong Yang }
1439*8af74909SZhong Yang 
Polygon(Point * pts,size_t npts,ColourDesired fore,ColourDesired back)1440*8af74909SZhong Yang void SurfaceD2D::Polygon(Point *pts, size_t npts, ColourDesired fore, ColourDesired back) {
1441*8af74909SZhong Yang 	PLATFORM_ASSERT(pRenderTarget && (npts > 2));
1442*8af74909SZhong Yang 	if (pRenderTarget) {
1443*8af74909SZhong Yang 		ID2D1PathGeometry *geometry = nullptr;
1444*8af74909SZhong Yang 		HRESULT hr = pD2DFactory->CreatePathGeometry(&geometry);
1445*8af74909SZhong Yang 		PLATFORM_ASSERT(geometry);
1446*8af74909SZhong Yang 		if (SUCCEEDED(hr) && geometry) {
1447*8af74909SZhong Yang 			ID2D1GeometrySink *sink = nullptr;
1448*8af74909SZhong Yang 			hr = geometry->Open(&sink);
1449*8af74909SZhong Yang 			PLATFORM_ASSERT(sink);
1450*8af74909SZhong Yang 			if (SUCCEEDED(hr) && sink) {
1451*8af74909SZhong Yang 				sink->BeginFigure(D2D1::Point2F(pts[0].x + 0.5f, pts[0].y + 0.5f), D2D1_FIGURE_BEGIN_FILLED);
1452*8af74909SZhong Yang 				for (size_t i=1; i<npts; i++) {
1453*8af74909SZhong Yang 					sink->AddLine(D2D1::Point2F(pts[i].x + 0.5f, pts[i].y + 0.5f));
1454*8af74909SZhong Yang 				}
1455*8af74909SZhong Yang 				sink->EndFigure(D2D1_FIGURE_END_CLOSED);
1456*8af74909SZhong Yang 				sink->Close();
1457*8af74909SZhong Yang 				ReleaseUnknown(sink);
1458*8af74909SZhong Yang 
1459*8af74909SZhong Yang 				D2DPenColour(back);
1460*8af74909SZhong Yang 				pRenderTarget->FillGeometry(geometry,pBrush);
1461*8af74909SZhong Yang 				D2DPenColour(fore);
1462*8af74909SZhong Yang 				pRenderTarget->DrawGeometry(geometry,pBrush);
1463*8af74909SZhong Yang 			}
1464*8af74909SZhong Yang 			ReleaseUnknown(geometry);
1465*8af74909SZhong Yang 		}
1466*8af74909SZhong Yang 	}
1467*8af74909SZhong Yang }
1468*8af74909SZhong Yang 
RectangleDraw(PRectangle rc,ColourDesired fore,ColourDesired back)1469*8af74909SZhong Yang void SurfaceD2D::RectangleDraw(PRectangle rc, ColourDesired fore, ColourDesired back) {
1470*8af74909SZhong Yang 	if (pRenderTarget) {
1471*8af74909SZhong Yang 		const D2D1_RECT_F rectangle1 = D2D1::RectF(std::round(rc.left) + 0.5f, rc.top+0.5f, std::round(rc.right) - 0.5f, rc.bottom-0.5f);
1472*8af74909SZhong Yang 		D2DPenColour(back);
1473*8af74909SZhong Yang 		pRenderTarget->FillRectangle(&rectangle1, pBrush);
1474*8af74909SZhong Yang 		D2DPenColour(fore);
1475*8af74909SZhong Yang 		pRenderTarget->DrawRectangle(&rectangle1, pBrush);
1476*8af74909SZhong Yang 	}
1477*8af74909SZhong Yang }
1478*8af74909SZhong Yang 
FillRectangle(PRectangle rc,ColourDesired back)1479*8af74909SZhong Yang void SurfaceD2D::FillRectangle(PRectangle rc, ColourDesired back) {
1480*8af74909SZhong Yang 	if (pRenderTarget) {
1481*8af74909SZhong Yang 		D2DPenColour(back);
1482*8af74909SZhong Yang 		const D2D1_RECT_F rectangle1 = D2D1::RectF(std::round(rc.left), rc.top, std::round(rc.right), rc.bottom);
1483*8af74909SZhong Yang 		pRenderTarget->FillRectangle(&rectangle1, pBrush);
1484*8af74909SZhong Yang 	}
1485*8af74909SZhong Yang }
1486*8af74909SZhong Yang 
FillRectangle(PRectangle rc,Surface & surfacePattern)1487*8af74909SZhong Yang void SurfaceD2D::FillRectangle(PRectangle rc, Surface &surfacePattern) {
1488*8af74909SZhong Yang 	SurfaceD2D *psurfOther = dynamic_cast<SurfaceD2D *>(&surfacePattern);
1489*8af74909SZhong Yang 	PLATFORM_ASSERT(psurfOther);
1490*8af74909SZhong Yang 	psurfOther->FlushDrawing();
1491*8af74909SZhong Yang 	ID2D1Bitmap *pBitmap = nullptr;
1492*8af74909SZhong Yang 	HRESULT hr = psurfOther->GetBitmap(&pBitmap);
1493*8af74909SZhong Yang 	if (SUCCEEDED(hr) && pBitmap) {
1494*8af74909SZhong Yang 		ID2D1BitmapBrush *pBitmapBrush = nullptr;
1495*8af74909SZhong Yang 		const D2D1_BITMAP_BRUSH_PROPERTIES brushProperties =
1496*8af74909SZhong Yang 	        D2D1::BitmapBrushProperties(D2D1_EXTEND_MODE_WRAP, D2D1_EXTEND_MODE_WRAP,
1497*8af74909SZhong Yang 			D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR);
1498*8af74909SZhong Yang 		// Create the bitmap brush.
1499*8af74909SZhong Yang 		hr = pRenderTarget->CreateBitmapBrush(pBitmap, brushProperties, &pBitmapBrush);
1500*8af74909SZhong Yang 		ReleaseUnknown(pBitmap);
1501*8af74909SZhong Yang 		if (SUCCEEDED(hr) && pBitmapBrush) {
1502*8af74909SZhong Yang 			pRenderTarget->FillRectangle(
1503*8af74909SZhong Yang 				D2D1::RectF(rc.left, rc.top, rc.right, rc.bottom),
1504*8af74909SZhong Yang 				pBitmapBrush);
1505*8af74909SZhong Yang 			ReleaseUnknown(pBitmapBrush);
1506*8af74909SZhong Yang 		}
1507*8af74909SZhong Yang 	}
1508*8af74909SZhong Yang }
1509*8af74909SZhong Yang 
RoundedRectangle(PRectangle rc,ColourDesired fore,ColourDesired back)1510*8af74909SZhong Yang void SurfaceD2D::RoundedRectangle(PRectangle rc, ColourDesired fore, ColourDesired back) {
1511*8af74909SZhong Yang 	if (pRenderTarget) {
1512*8af74909SZhong Yang 		D2D1_ROUNDED_RECT roundedRectFill = {
1513*8af74909SZhong Yang 			D2D1::RectF(rc.left+1.0f, rc.top+1.0f, rc.right-1.0f, rc.bottom-1.0f),
1514*8af74909SZhong Yang 			4, 4};
1515*8af74909SZhong Yang 		D2DPenColour(back);
1516*8af74909SZhong Yang 		pRenderTarget->FillRoundedRectangle(roundedRectFill, pBrush);
1517*8af74909SZhong Yang 
1518*8af74909SZhong Yang 		D2D1_ROUNDED_RECT roundedRect = {
1519*8af74909SZhong Yang 			D2D1::RectF(rc.left + 0.5f, rc.top+0.5f, rc.right - 0.5f, rc.bottom-0.5f),
1520*8af74909SZhong Yang 			4, 4};
1521*8af74909SZhong Yang 		D2DPenColour(fore);
1522*8af74909SZhong Yang 		pRenderTarget->DrawRoundedRectangle(roundedRect, pBrush);
1523*8af74909SZhong Yang 	}
1524*8af74909SZhong Yang }
1525*8af74909SZhong Yang 
AlphaRectangle(PRectangle rc,int cornerSize,ColourDesired fill,int alphaFill,ColourDesired outline,int alphaOutline,int)1526*8af74909SZhong Yang void SurfaceD2D::AlphaRectangle(PRectangle rc, int cornerSize, ColourDesired fill, int alphaFill,
1527*8af74909SZhong Yang 		ColourDesired outline, int alphaOutline, int /* flags*/ ) {
1528*8af74909SZhong Yang 	if (pRenderTarget) {
1529*8af74909SZhong Yang 		if (cornerSize == 0) {
1530*8af74909SZhong Yang 			// When corner size is zero, draw square rectangle to prevent blurry pixels at corners
1531*8af74909SZhong Yang 			const D2D1_RECT_F rectFill = D2D1::RectF(std::round(rc.left) + 1.0f, rc.top + 1.0f, std::round(rc.right) - 1.0f, rc.bottom - 1.0f);
1532*8af74909SZhong Yang 			D2DPenColour(fill, alphaFill);
1533*8af74909SZhong Yang 			pRenderTarget->FillRectangle(rectFill, pBrush);
1534*8af74909SZhong Yang 
1535*8af74909SZhong Yang 			const D2D1_RECT_F rectOutline = D2D1::RectF(std::round(rc.left) + 0.5f, rc.top + 0.5f, std::round(rc.right) - 0.5f, rc.bottom - 0.5f);
1536*8af74909SZhong Yang 			D2DPenColour(outline, alphaOutline);
1537*8af74909SZhong Yang 			pRenderTarget->DrawRectangle(rectOutline, pBrush);
1538*8af74909SZhong Yang 		} else {
1539*8af74909SZhong Yang 			const float cornerSizeF = static_cast<float>(cornerSize);
1540*8af74909SZhong Yang 			D2D1_ROUNDED_RECT roundedRectFill = {
1541*8af74909SZhong Yang 				D2D1::RectF(std::round(rc.left) + 1.0f, rc.top + 1.0f, std::round(rc.right) - 1.0f, rc.bottom - 1.0f),
1542*8af74909SZhong Yang 				cornerSizeF - 1.0f, cornerSizeF - 1.0f };
1543*8af74909SZhong Yang 			D2DPenColour(fill, alphaFill);
1544*8af74909SZhong Yang 			pRenderTarget->FillRoundedRectangle(roundedRectFill, pBrush);
1545*8af74909SZhong Yang 
1546*8af74909SZhong Yang 			D2D1_ROUNDED_RECT roundedRect = {
1547*8af74909SZhong Yang 				D2D1::RectF(std::round(rc.left) + 0.5f, rc.top + 0.5f, std::round(rc.right) - 0.5f, rc.bottom - 0.5f),
1548*8af74909SZhong Yang 				cornerSizeF, cornerSizeF};
1549*8af74909SZhong Yang 			D2DPenColour(outline, alphaOutline);
1550*8af74909SZhong Yang 			pRenderTarget->DrawRoundedRectangle(roundedRect, pBrush);
1551*8af74909SZhong Yang 		}
1552*8af74909SZhong Yang 	}
1553*8af74909SZhong Yang }
1554*8af74909SZhong Yang 
1555*8af74909SZhong Yang namespace {
1556*8af74909SZhong Yang 
ColorFromColourAlpha(ColourAlpha colour)1557*8af74909SZhong Yang D2D_COLOR_F ColorFromColourAlpha(ColourAlpha colour) noexcept {
1558*8af74909SZhong Yang 	D2D_COLOR_F col;
1559*8af74909SZhong Yang 	col.r = colour.GetRedComponent();
1560*8af74909SZhong Yang 	col.g = colour.GetGreenComponent();
1561*8af74909SZhong Yang 	col.b = colour.GetBlueComponent();
1562*8af74909SZhong Yang 	col.a = colour.GetAlphaComponent();
1563*8af74909SZhong Yang 	return col;
1564*8af74909SZhong Yang }
1565*8af74909SZhong Yang 
1566*8af74909SZhong Yang }
1567*8af74909SZhong Yang 
GradientRectangle(PRectangle rc,const std::vector<ColourStop> & stops,GradientOptions options)1568*8af74909SZhong Yang void SurfaceD2D::GradientRectangle(PRectangle rc, const std::vector<ColourStop> &stops, GradientOptions options) {
1569*8af74909SZhong Yang 	if (pRenderTarget) {
1570*8af74909SZhong Yang 		D2D1_LINEAR_GRADIENT_BRUSH_PROPERTIES lgbp;
1571*8af74909SZhong Yang 		lgbp.startPoint = D2D1::Point2F(rc.left, rc.top);
1572*8af74909SZhong Yang 		switch (options) {
1573*8af74909SZhong Yang 		case GradientOptions::leftToRight:
1574*8af74909SZhong Yang 			lgbp.endPoint = D2D1::Point2F(rc.right, rc.top);
1575*8af74909SZhong Yang 			break;
1576*8af74909SZhong Yang 		case GradientOptions::topToBottom:
1577*8af74909SZhong Yang 		default:
1578*8af74909SZhong Yang 			lgbp.endPoint = D2D1::Point2F(rc.left, rc.bottom);
1579*8af74909SZhong Yang 			break;
1580*8af74909SZhong Yang 		}
1581*8af74909SZhong Yang 
1582*8af74909SZhong Yang 		std::vector<D2D1_GRADIENT_STOP> gradientStops;
1583*8af74909SZhong Yang 		for (const ColourStop &stop : stops) {
1584*8af74909SZhong Yang 			gradientStops.push_back({ stop.position, ColorFromColourAlpha(stop.colour) });
1585*8af74909SZhong Yang 		}
1586*8af74909SZhong Yang 		ID2D1GradientStopCollection *pGradientStops = nullptr;
1587*8af74909SZhong Yang 		HRESULT hr = pRenderTarget->CreateGradientStopCollection(
1588*8af74909SZhong Yang 			gradientStops.data(), static_cast<UINT32>(gradientStops.size()), &pGradientStops);
1589*8af74909SZhong Yang 		if (FAILED(hr) || !pGradientStops) {
1590*8af74909SZhong Yang 			return;
1591*8af74909SZhong Yang 		}
1592*8af74909SZhong Yang 		ID2D1LinearGradientBrush *pBrushLinear = nullptr;
1593*8af74909SZhong Yang 		hr = pRenderTarget->CreateLinearGradientBrush(
1594*8af74909SZhong Yang 			lgbp, pGradientStops, &pBrushLinear);
1595*8af74909SZhong Yang 		if (SUCCEEDED(hr) && pBrushLinear) {
1596*8af74909SZhong Yang 			const D2D1_RECT_F rectangle = D2D1::RectF(std::round(rc.left), rc.top, std::round(rc.right), rc.bottom);
1597*8af74909SZhong Yang 			pRenderTarget->FillRectangle(&rectangle, pBrushLinear);
1598*8af74909SZhong Yang 			ReleaseUnknown(pBrushLinear);
1599*8af74909SZhong Yang 		}
1600*8af74909SZhong Yang 		ReleaseUnknown(pGradientStops);
1601*8af74909SZhong Yang 	}
1602*8af74909SZhong Yang }
1603*8af74909SZhong Yang 
DrawRGBAImage(PRectangle rc,int width,int height,const unsigned char * pixelsImage)1604*8af74909SZhong Yang void SurfaceD2D::DrawRGBAImage(PRectangle rc, int width, int height, const unsigned char *pixelsImage) {
1605*8af74909SZhong Yang 	if (pRenderTarget) {
1606*8af74909SZhong Yang 		if (rc.Width() > width)
1607*8af74909SZhong Yang 			rc.left += std::floor((rc.Width() - width) / 2);
1608*8af74909SZhong Yang 		rc.right = rc.left + width;
1609*8af74909SZhong Yang 		if (rc.Height() > height)
1610*8af74909SZhong Yang 			rc.top += std::floor((rc.Height() - height) / 2);
1611*8af74909SZhong Yang 		rc.bottom = rc.top + height;
1612*8af74909SZhong Yang 
1613*8af74909SZhong Yang 		std::vector<unsigned char> image(RGBAImage::bytesPerPixel * height * width);
1614*8af74909SZhong Yang 		RGBAImage::BGRAFromRGBA(image.data(), pixelsImage, static_cast<ptrdiff_t>(height) * width);
1615*8af74909SZhong Yang 
1616*8af74909SZhong Yang 		ID2D1Bitmap *bitmap = nullptr;
1617*8af74909SZhong Yang 		const D2D1_SIZE_U size = D2D1::SizeU(width, height);
1618*8af74909SZhong Yang 		D2D1_BITMAP_PROPERTIES props = {{DXGI_FORMAT_B8G8R8A8_UNORM,
1619*8af74909SZhong Yang 		    D2D1_ALPHA_MODE_PREMULTIPLIED}, 72.0, 72.0};
1620*8af74909SZhong Yang 		const HRESULT hr = pRenderTarget->CreateBitmap(size, image.data(),
1621*8af74909SZhong Yang                   width * 4, &props, &bitmap);
1622*8af74909SZhong Yang 		if (SUCCEEDED(hr)) {
1623*8af74909SZhong Yang 			const D2D1_RECT_F rcDestination = RectangleFromPRectangle(rc);
1624*8af74909SZhong Yang 			pRenderTarget->DrawBitmap(bitmap, rcDestination);
1625*8af74909SZhong Yang 			ReleaseUnknown(bitmap);
1626*8af74909SZhong Yang 		}
1627*8af74909SZhong Yang 	}
1628*8af74909SZhong Yang }
1629*8af74909SZhong Yang 
Ellipse(PRectangle rc,ColourDesired fore,ColourDesired back)1630*8af74909SZhong Yang void SurfaceD2D::Ellipse(PRectangle rc, ColourDesired fore, ColourDesired back) {
1631*8af74909SZhong Yang 	if (pRenderTarget) {
1632*8af74909SZhong Yang 		const FLOAT radius = rc.Width() / 2.0f;
1633*8af74909SZhong Yang 		D2D1_ELLIPSE ellipse = {
1634*8af74909SZhong Yang 			D2D1::Point2F((rc.left + rc.right) / 2.0f, (rc.top + rc.bottom) / 2.0f),
1635*8af74909SZhong Yang 			radius,radius};
1636*8af74909SZhong Yang 
1637*8af74909SZhong Yang 		PenColour(back);
1638*8af74909SZhong Yang 		pRenderTarget->FillEllipse(ellipse, pBrush);
1639*8af74909SZhong Yang 		PenColour(fore);
1640*8af74909SZhong Yang 		pRenderTarget->DrawEllipse(ellipse, pBrush);
1641*8af74909SZhong Yang 	}
1642*8af74909SZhong Yang }
1643*8af74909SZhong Yang 
Copy(PRectangle rc,Point from,Surface & surfaceSource)1644*8af74909SZhong Yang void SurfaceD2D::Copy(PRectangle rc, Point from, Surface &surfaceSource) {
1645*8af74909SZhong Yang 	SurfaceD2D &surfOther = dynamic_cast<SurfaceD2D &>(surfaceSource);
1646*8af74909SZhong Yang 	surfOther.FlushDrawing();
1647*8af74909SZhong Yang 	ID2D1Bitmap *pBitmap = nullptr;
1648*8af74909SZhong Yang 	HRESULT hr = surfOther.GetBitmap(&pBitmap);
1649*8af74909SZhong Yang 	if (SUCCEEDED(hr) && pBitmap) {
1650*8af74909SZhong Yang 		const D2D1_RECT_F rcDestination = RectangleFromPRectangle(rc);
1651*8af74909SZhong Yang 		D2D1_RECT_F rcSource = {from.x, from.y, from.x + rc.Width(), from.y + rc.Height()};
1652*8af74909SZhong Yang 		pRenderTarget->DrawBitmap(pBitmap, rcDestination, 1.0f,
1653*8af74909SZhong Yang 			D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR, rcSource);
1654*8af74909SZhong Yang 		hr = pRenderTarget->Flush();
1655*8af74909SZhong Yang 		if (FAILED(hr)) {
1656*8af74909SZhong Yang 			Platform::DebugPrintf("Failed Flush 0x%lx\n", hr);
1657*8af74909SZhong Yang 		}
1658*8af74909SZhong Yang 		ReleaseUnknown(pBitmap);
1659*8af74909SZhong Yang 	}
1660*8af74909SZhong Yang }
1661*8af74909SZhong Yang 
1662*8af74909SZhong Yang class BlobInline : public IDWriteInlineObject {
1663*8af74909SZhong Yang 	XYPOSITION width;
1664*8af74909SZhong Yang 
1665*8af74909SZhong Yang 	// IUnknown
1666*8af74909SZhong Yang 	STDMETHODIMP QueryInterface(REFIID riid, PVOID *ppv) override;
1667*8af74909SZhong Yang 	STDMETHODIMP_(ULONG)AddRef() override;
1668*8af74909SZhong Yang 	STDMETHODIMP_(ULONG)Release() override;
1669*8af74909SZhong Yang 
1670*8af74909SZhong Yang 	// IDWriteInlineObject
1671*8af74909SZhong Yang 	COM_DECLSPEC_NOTHROW STDMETHODIMP Draw(
1672*8af74909SZhong Yang 		void *clientDrawingContext,
1673*8af74909SZhong Yang 		IDWriteTextRenderer *renderer,
1674*8af74909SZhong Yang 		FLOAT originX,
1675*8af74909SZhong Yang 		FLOAT originY,
1676*8af74909SZhong Yang 		BOOL isSideways,
1677*8af74909SZhong Yang 		BOOL isRightToLeft,
1678*8af74909SZhong Yang 		IUnknown *clientDrawingEffect
1679*8af74909SZhong Yang 		) override;
1680*8af74909SZhong Yang 	COM_DECLSPEC_NOTHROW STDMETHODIMP GetMetrics(DWRITE_INLINE_OBJECT_METRICS *metrics) override;
1681*8af74909SZhong Yang 	COM_DECLSPEC_NOTHROW STDMETHODIMP GetOverhangMetrics(DWRITE_OVERHANG_METRICS *overhangs) override;
1682*8af74909SZhong Yang 	COM_DECLSPEC_NOTHROW STDMETHODIMP GetBreakConditions(
1683*8af74909SZhong Yang 		DWRITE_BREAK_CONDITION *breakConditionBefore,
1684*8af74909SZhong Yang 		DWRITE_BREAK_CONDITION *breakConditionAfter) override;
1685*8af74909SZhong Yang public:
BlobInline(XYPOSITION width_=0.0f)1686*8af74909SZhong Yang 	BlobInline(XYPOSITION width_=0.0f) noexcept : width(width_) {
1687*8af74909SZhong Yang 	}
1688*8af74909SZhong Yang 	// Defaulted so BlobInline objects can be copied.
1689*8af74909SZhong Yang 	BlobInline(const BlobInline &) = default;
1690*8af74909SZhong Yang 	BlobInline(BlobInline &&) = default;
1691*8af74909SZhong Yang 	BlobInline &operator=(const BlobInline &) = default;
1692*8af74909SZhong Yang 	BlobInline &operator=(BlobInline &&) = default;
~BlobInline()1693*8af74909SZhong Yang 	virtual ~BlobInline() {
1694*8af74909SZhong Yang 	}
1695*8af74909SZhong Yang };
1696*8af74909SZhong Yang 
1697*8af74909SZhong Yang /// Implement IUnknown
QueryInterface(REFIID riid,PVOID * ppv)1698*8af74909SZhong Yang STDMETHODIMP BlobInline::QueryInterface(REFIID riid, PVOID *ppv) {
1699*8af74909SZhong Yang 	if (!ppv)
1700*8af74909SZhong Yang 		return E_POINTER;
1701*8af74909SZhong Yang 	// Never called so not checked.
1702*8af74909SZhong Yang 	*ppv = nullptr;
1703*8af74909SZhong Yang 	if (riid == IID_IUnknown)
1704*8af74909SZhong Yang 		*ppv = static_cast<IUnknown *>(this);
1705*8af74909SZhong Yang 	if (riid == __uuidof(IDWriteInlineObject))
1706*8af74909SZhong Yang 		*ppv = static_cast<IDWriteInlineObject *>(this);
1707*8af74909SZhong Yang 	if (!*ppv)
1708*8af74909SZhong Yang 		return E_NOINTERFACE;
1709*8af74909SZhong Yang 	return S_OK;
1710*8af74909SZhong Yang }
1711*8af74909SZhong Yang 
STDMETHODIMP_(ULONG)1712*8af74909SZhong Yang STDMETHODIMP_(ULONG) BlobInline::AddRef() {
1713*8af74909SZhong Yang 	// Lifetime tied to Platform methods so ignore any reference operations.
1714*8af74909SZhong Yang 	return 1;
1715*8af74909SZhong Yang }
1716*8af74909SZhong Yang 
STDMETHODIMP_(ULONG)1717*8af74909SZhong Yang STDMETHODIMP_(ULONG) BlobInline::Release() {
1718*8af74909SZhong Yang 	// Lifetime tied to Platform methods so ignore any reference operations.
1719*8af74909SZhong Yang 	return 1;
1720*8af74909SZhong Yang }
1721*8af74909SZhong Yang 
1722*8af74909SZhong Yang /// Implement IDWriteInlineObject
Draw(void *,IDWriteTextRenderer *,FLOAT,FLOAT,BOOL,BOOL,IUnknown *)1723*8af74909SZhong Yang COM_DECLSPEC_NOTHROW HRESULT STDMETHODCALLTYPE BlobInline::Draw(
1724*8af74909SZhong Yang 	void*,
1725*8af74909SZhong Yang 	IDWriteTextRenderer*,
1726*8af74909SZhong Yang 	FLOAT,
1727*8af74909SZhong Yang 	FLOAT,
1728*8af74909SZhong Yang 	BOOL,
1729*8af74909SZhong Yang 	BOOL,
1730*8af74909SZhong Yang 	IUnknown*) {
1731*8af74909SZhong Yang 	// Since not performing drawing, not necessary to implement
1732*8af74909SZhong Yang 	// Could be implemented by calling back into platform-independent code.
1733*8af74909SZhong Yang 	// This would allow more of the drawing to be mediated through DirectWrite.
1734*8af74909SZhong Yang 	return S_OK;
1735*8af74909SZhong Yang }
1736*8af74909SZhong Yang 
GetMetrics(DWRITE_INLINE_OBJECT_METRICS * metrics)1737*8af74909SZhong Yang COM_DECLSPEC_NOTHROW HRESULT STDMETHODCALLTYPE BlobInline::GetMetrics(
1738*8af74909SZhong Yang 	DWRITE_INLINE_OBJECT_METRICS *metrics
1739*8af74909SZhong Yang ) {
1740*8af74909SZhong Yang 	if (!metrics)
1741*8af74909SZhong Yang 		return E_POINTER;
1742*8af74909SZhong Yang 	metrics->width = width;
1743*8af74909SZhong Yang 	metrics->height = 2;
1744*8af74909SZhong Yang 	metrics->baseline = 1;
1745*8af74909SZhong Yang 	metrics->supportsSideways = FALSE;
1746*8af74909SZhong Yang 	return S_OK;
1747*8af74909SZhong Yang }
1748*8af74909SZhong Yang 
GetOverhangMetrics(DWRITE_OVERHANG_METRICS * overhangs)1749*8af74909SZhong Yang COM_DECLSPEC_NOTHROW HRESULT STDMETHODCALLTYPE BlobInline::GetOverhangMetrics(
1750*8af74909SZhong Yang 	DWRITE_OVERHANG_METRICS *overhangs
1751*8af74909SZhong Yang ) {
1752*8af74909SZhong Yang 	if (!overhangs)
1753*8af74909SZhong Yang 		return E_POINTER;
1754*8af74909SZhong Yang 	overhangs->left = 0;
1755*8af74909SZhong Yang 	overhangs->top = 0;
1756*8af74909SZhong Yang 	overhangs->right = 0;
1757*8af74909SZhong Yang 	overhangs->bottom = 0;
1758*8af74909SZhong Yang 	return S_OK;
1759*8af74909SZhong Yang }
1760*8af74909SZhong Yang 
GetBreakConditions(DWRITE_BREAK_CONDITION * breakConditionBefore,DWRITE_BREAK_CONDITION * breakConditionAfter)1761*8af74909SZhong Yang COM_DECLSPEC_NOTHROW HRESULT STDMETHODCALLTYPE BlobInline::GetBreakConditions(
1762*8af74909SZhong Yang 	DWRITE_BREAK_CONDITION *breakConditionBefore,
1763*8af74909SZhong Yang 	DWRITE_BREAK_CONDITION *breakConditionAfter
1764*8af74909SZhong Yang ) {
1765*8af74909SZhong Yang 	if (!breakConditionBefore || !breakConditionAfter)
1766*8af74909SZhong Yang 		return E_POINTER;
1767*8af74909SZhong Yang 	// Since not performing 2D layout, not necessary to implement
1768*8af74909SZhong Yang 	*breakConditionBefore = DWRITE_BREAK_CONDITION_NEUTRAL;
1769*8af74909SZhong Yang 	*breakConditionAfter = DWRITE_BREAK_CONDITION_NEUTRAL;
1770*8af74909SZhong Yang 	return S_OK;
1771*8af74909SZhong Yang }
1772*8af74909SZhong Yang 
1773*8af74909SZhong Yang class ScreenLineLayout : public IScreenLineLayout {
1774*8af74909SZhong Yang 	IDWriteTextLayout *textLayout = nullptr;
1775*8af74909SZhong Yang 	std::string text;
1776*8af74909SZhong Yang 	std::wstring buffer;
1777*8af74909SZhong Yang 	std::vector<BlobInline> blobs;
1778*8af74909SZhong Yang 	static void FillTextLayoutFormats(const IScreenLine *screenLine, IDWriteTextLayout *textLayout, std::vector<BlobInline> &blobs);
1779*8af74909SZhong Yang 	static std::wstring ReplaceRepresentation(std::string_view text);
1780*8af74909SZhong Yang 	static size_t GetPositionInLayout(std::string_view text, size_t position);
1781*8af74909SZhong Yang public:
1782*8af74909SZhong Yang 	ScreenLineLayout(const IScreenLine *screenLine);
1783*8af74909SZhong Yang 	// Deleted so ScreenLineLayout objects can not be copied
1784*8af74909SZhong Yang 	ScreenLineLayout(const ScreenLineLayout &) = delete;
1785*8af74909SZhong Yang 	ScreenLineLayout(ScreenLineLayout &&) = delete;
1786*8af74909SZhong Yang 	ScreenLineLayout &operator=(const ScreenLineLayout &) = delete;
1787*8af74909SZhong Yang 	ScreenLineLayout &operator=(ScreenLineLayout &&) = delete;
1788*8af74909SZhong Yang 	~ScreenLineLayout() override;
1789*8af74909SZhong Yang 	size_t PositionFromX(XYPOSITION xDistance, bool charPosition) override;
1790*8af74909SZhong Yang 	XYPOSITION XFromPosition(size_t caretPosition) override;
1791*8af74909SZhong Yang 	std::vector<Interval> FindRangeIntervals(size_t start, size_t end) override;
1792*8af74909SZhong Yang };
1793*8af74909SZhong Yang 
1794*8af74909SZhong Yang // Each char can have its own style, so we fill the textLayout with the textFormat of each char
1795*8af74909SZhong Yang 
FillTextLayoutFormats(const IScreenLine * screenLine,IDWriteTextLayout * textLayout,std::vector<BlobInline> & blobs)1796*8af74909SZhong Yang void ScreenLineLayout::FillTextLayoutFormats(const IScreenLine *screenLine, IDWriteTextLayout *textLayout, std::vector<BlobInline> &blobs) {
1797*8af74909SZhong Yang 	// Reserve enough entries up front so they are not moved and the pointers handed
1798*8af74909SZhong Yang 	// to textLayout remain valid.
1799*8af74909SZhong Yang 	const ptrdiff_t numRepresentations = screenLine->RepresentationCount();
1800*8af74909SZhong Yang 	std::string_view text = screenLine->Text();
1801*8af74909SZhong Yang 	const ptrdiff_t numTabs = std::count(std::begin(text), std::end(text), '\t');
1802*8af74909SZhong Yang 	blobs.reserve(numRepresentations + numTabs);
1803*8af74909SZhong Yang 
1804*8af74909SZhong Yang 	UINT32 layoutPosition = 0;
1805*8af74909SZhong Yang 
1806*8af74909SZhong Yang 	for (size_t bytePosition = 0; bytePosition < screenLine->Length();) {
1807*8af74909SZhong Yang 		const unsigned char uch = screenLine->Text()[bytePosition];
1808*8af74909SZhong Yang 		const unsigned int byteCount = UTF8BytesOfLead[uch];
1809*8af74909SZhong Yang 		const UINT32 codeUnits = UTF16LengthFromUTF8ByteCount(byteCount);
1810*8af74909SZhong Yang 		const DWRITE_TEXT_RANGE textRange = { layoutPosition, codeUnits };
1811*8af74909SZhong Yang 
1812*8af74909SZhong Yang 		XYPOSITION representationWidth = screenLine->RepresentationWidth(bytePosition);
1813*8af74909SZhong Yang 		if ((representationWidth == 0.0f) && (screenLine->Text()[bytePosition] == '\t')) {
1814*8af74909SZhong Yang 			Point realPt;
1815*8af74909SZhong Yang 			DWRITE_HIT_TEST_METRICS realCaretMetrics {};
1816*8af74909SZhong Yang 			textLayout->HitTestTextPosition(
1817*8af74909SZhong Yang 				layoutPosition,
1818*8af74909SZhong Yang 				false, // trailing if false, else leading edge
1819*8af74909SZhong Yang 				&realPt.x,
1820*8af74909SZhong Yang 				&realPt.y,
1821*8af74909SZhong Yang 				&realCaretMetrics
1822*8af74909SZhong Yang 			);
1823*8af74909SZhong Yang 
1824*8af74909SZhong Yang 			const XYPOSITION nextTab = screenLine->TabPositionAfter(realPt.x);
1825*8af74909SZhong Yang 			representationWidth = nextTab - realPt.x;
1826*8af74909SZhong Yang 		}
1827*8af74909SZhong Yang 		if (representationWidth > 0.0f) {
1828*8af74909SZhong Yang 			blobs.push_back(BlobInline(representationWidth));
1829*8af74909SZhong Yang 			textLayout->SetInlineObject(&blobs.back(), textRange);
1830*8af74909SZhong Yang 		};
1831*8af74909SZhong Yang 
1832*8af74909SZhong Yang 		FormatAndMetrics *pfm =
1833*8af74909SZhong Yang 			static_cast<FormatAndMetrics *>(screenLine->FontOfPosition(bytePosition)->GetID());
1834*8af74909SZhong Yang 
1835*8af74909SZhong Yang 		const unsigned int fontFamilyNameSize = pfm->pTextFormat->GetFontFamilyNameLength();
1836*8af74909SZhong Yang 		std::wstring fontFamilyName(fontFamilyNameSize, 0);
1837*8af74909SZhong Yang 		const HRESULT hrFamily = pfm->pTextFormat->GetFontFamilyName(fontFamilyName.data(), fontFamilyNameSize + 1);
1838*8af74909SZhong Yang 		if (SUCCEEDED(hrFamily)) {
1839*8af74909SZhong Yang 			textLayout->SetFontFamilyName(fontFamilyName.c_str(), textRange);
1840*8af74909SZhong Yang 		}
1841*8af74909SZhong Yang 
1842*8af74909SZhong Yang 		textLayout->SetFontSize(pfm->pTextFormat->GetFontSize(), textRange);
1843*8af74909SZhong Yang 		textLayout->SetFontWeight(pfm->pTextFormat->GetFontWeight(), textRange);
1844*8af74909SZhong Yang 		textLayout->SetFontStyle(pfm->pTextFormat->GetFontStyle(), textRange);
1845*8af74909SZhong Yang 
1846*8af74909SZhong Yang 		const unsigned int localeNameSize = pfm->pTextFormat->GetLocaleNameLength();
1847*8af74909SZhong Yang 		std::wstring localeName(localeNameSize, 0);
1848*8af74909SZhong Yang 		const HRESULT hrLocale = pfm->pTextFormat->GetLocaleName(localeName.data(), localeNameSize + 1);
1849*8af74909SZhong Yang 		if (SUCCEEDED(hrLocale)) {
1850*8af74909SZhong Yang 			textLayout->SetLocaleName(localeName.c_str(), textRange);
1851*8af74909SZhong Yang 		}
1852*8af74909SZhong Yang 
1853*8af74909SZhong Yang 		textLayout->SetFontStretch(pfm->pTextFormat->GetFontStretch(), textRange);
1854*8af74909SZhong Yang 
1855*8af74909SZhong Yang 		IDWriteFontCollection *fontCollection = nullptr;
1856*8af74909SZhong Yang 		if (SUCCEEDED(pfm->pTextFormat->GetFontCollection(&fontCollection))) {
1857*8af74909SZhong Yang 			textLayout->SetFontCollection(fontCollection, textRange);
1858*8af74909SZhong Yang 		}
1859*8af74909SZhong Yang 
1860*8af74909SZhong Yang 		bytePosition += byteCount;
1861*8af74909SZhong Yang 		layoutPosition += codeUnits;
1862*8af74909SZhong Yang 	}
1863*8af74909SZhong Yang 
1864*8af74909SZhong Yang }
1865*8af74909SZhong Yang 
1866*8af74909SZhong Yang /* Convert to a wide character string and replace tabs with X to stop DirectWrite tab expansion */
1867*8af74909SZhong Yang 
ReplaceRepresentation(std::string_view text)1868*8af74909SZhong Yang std::wstring ScreenLineLayout::ReplaceRepresentation(std::string_view text) {
1869*8af74909SZhong Yang 	const TextWide wideText(text, true);
1870*8af74909SZhong Yang 	std::wstring ws(wideText.buffer, wideText.tlen);
1871*8af74909SZhong Yang 	std::replace(ws.begin(), ws.end(), L'\t', L'X');
1872*8af74909SZhong Yang 	return ws;
1873*8af74909SZhong Yang }
1874*8af74909SZhong Yang 
1875*8af74909SZhong Yang // Finds the position in the wide character version of the text.
1876*8af74909SZhong Yang 
GetPositionInLayout(std::string_view text,size_t position)1877*8af74909SZhong Yang size_t ScreenLineLayout::GetPositionInLayout(std::string_view text, size_t position) {
1878*8af74909SZhong Yang 	const std::string_view textUptoPosition = text.substr(0, position);
1879*8af74909SZhong Yang 	return UTF16Length(textUptoPosition);
1880*8af74909SZhong Yang }
1881*8af74909SZhong Yang 
ScreenLineLayout(const IScreenLine * screenLine)1882*8af74909SZhong Yang ScreenLineLayout::ScreenLineLayout(const IScreenLine *screenLine) {
1883*8af74909SZhong Yang 	// If the text is empty, then no need to go through this function
1884*8af74909SZhong Yang 	if (!screenLine || !screenLine->Length())
1885*8af74909SZhong Yang 		return;
1886*8af74909SZhong Yang 
1887*8af74909SZhong Yang 	text = screenLine->Text();
1888*8af74909SZhong Yang 
1889*8af74909SZhong Yang 	// Get textFormat
1890*8af74909SZhong Yang 	FormatAndMetrics *pfm = static_cast<FormatAndMetrics *>(screenLine->FontOfPosition(0)->GetID());
1891*8af74909SZhong Yang 
1892*8af74909SZhong Yang 	if (!pIDWriteFactory || !pfm->pTextFormat) {
1893*8af74909SZhong Yang 		return;
1894*8af74909SZhong Yang 	}
1895*8af74909SZhong Yang 
1896*8af74909SZhong Yang 	// Convert the string to wstring and replace the original control characters with their representative chars.
1897*8af74909SZhong Yang 	buffer = ReplaceRepresentation(screenLine->Text());
1898*8af74909SZhong Yang 
1899*8af74909SZhong Yang 	// Create a text layout
1900*8af74909SZhong Yang 	const HRESULT hrCreate = pIDWriteFactory->CreateTextLayout(buffer.c_str(), static_cast<UINT32>(buffer.length()),
1901*8af74909SZhong Yang 		pfm->pTextFormat, screenLine->Width(), screenLine->Height(), &textLayout);
1902*8af74909SZhong Yang 	if (!SUCCEEDED(hrCreate)) {
1903*8af74909SZhong Yang 		return;
1904*8af74909SZhong Yang 	}
1905*8af74909SZhong Yang 
1906*8af74909SZhong Yang 	// Fill the textLayout chars with their own formats
1907*8af74909SZhong Yang 	FillTextLayoutFormats(screenLine, textLayout, blobs);
1908*8af74909SZhong Yang }
1909*8af74909SZhong Yang 
~ScreenLineLayout()1910*8af74909SZhong Yang ScreenLineLayout::~ScreenLineLayout() {
1911*8af74909SZhong Yang 	ReleaseUnknown(textLayout);
1912*8af74909SZhong Yang }
1913*8af74909SZhong Yang 
1914*8af74909SZhong Yang // Get the position from the provided x
1915*8af74909SZhong Yang 
PositionFromX(XYPOSITION xDistance,bool charPosition)1916*8af74909SZhong Yang size_t ScreenLineLayout::PositionFromX(XYPOSITION xDistance, bool charPosition) {
1917*8af74909SZhong Yang 	if (!textLayout) {
1918*8af74909SZhong Yang 		return 0;
1919*8af74909SZhong Yang 	}
1920*8af74909SZhong Yang 
1921*8af74909SZhong Yang 	// Returns the text position corresponding to the mouse x,y.
1922*8af74909SZhong Yang 	// If hitting the trailing side of a cluster, return the
1923*8af74909SZhong Yang 	// leading edge of the following text position.
1924*8af74909SZhong Yang 
1925*8af74909SZhong Yang 	BOOL isTrailingHit = FALSE;
1926*8af74909SZhong Yang 	BOOL isInside = FALSE;
1927*8af74909SZhong Yang 	DWRITE_HIT_TEST_METRICS caretMetrics {};
1928*8af74909SZhong Yang 
1929*8af74909SZhong Yang 	textLayout->HitTestPoint(
1930*8af74909SZhong Yang 		xDistance,
1931*8af74909SZhong Yang 		0.0f,
1932*8af74909SZhong Yang 		&isTrailingHit,
1933*8af74909SZhong Yang 		&isInside,
1934*8af74909SZhong Yang 		&caretMetrics
1935*8af74909SZhong Yang 	);
1936*8af74909SZhong Yang 
1937*8af74909SZhong Yang 	DWRITE_HIT_TEST_METRICS hitTestMetrics {};
1938*8af74909SZhong Yang 	if (isTrailingHit) {
1939*8af74909SZhong Yang 		FLOAT caretX = 0.0f;
1940*8af74909SZhong Yang 		FLOAT caretY = 0.0f;
1941*8af74909SZhong Yang 
1942*8af74909SZhong Yang 		// Uses hit-testing to align the current caret position to a whole cluster,
1943*8af74909SZhong Yang 		// rather than residing in the middle of a base character + diacritic,
1944*8af74909SZhong Yang 		// surrogate pair, or character + UVS.
1945*8af74909SZhong Yang 
1946*8af74909SZhong Yang 		// Align the caret to the nearest whole cluster.
1947*8af74909SZhong Yang 		textLayout->HitTestTextPosition(
1948*8af74909SZhong Yang 			caretMetrics.textPosition,
1949*8af74909SZhong Yang 			false,
1950*8af74909SZhong Yang 			&caretX,
1951*8af74909SZhong Yang 			&caretY,
1952*8af74909SZhong Yang 			&hitTestMetrics
1953*8af74909SZhong Yang 		);
1954*8af74909SZhong Yang 	}
1955*8af74909SZhong Yang 
1956*8af74909SZhong Yang 	size_t pos;
1957*8af74909SZhong Yang 	if (charPosition) {
1958*8af74909SZhong Yang 		pos = isTrailingHit ? hitTestMetrics.textPosition : caretMetrics.textPosition;
1959*8af74909SZhong Yang 	} else {
1960*8af74909SZhong Yang 		pos = isTrailingHit ? hitTestMetrics.textPosition + hitTestMetrics.length : caretMetrics.textPosition;
1961*8af74909SZhong Yang 	}
1962*8af74909SZhong Yang 
1963*8af74909SZhong Yang 	// Get the character position in original string
1964*8af74909SZhong Yang 	return UTF8PositionFromUTF16Position(text, pos);
1965*8af74909SZhong Yang }
1966*8af74909SZhong Yang 
1967*8af74909SZhong Yang // Finds the point of the caret position
1968*8af74909SZhong Yang 
XFromPosition(size_t caretPosition)1969*8af74909SZhong Yang XYPOSITION ScreenLineLayout::XFromPosition(size_t caretPosition) {
1970*8af74909SZhong Yang 	if (!textLayout) {
1971*8af74909SZhong Yang 		return 0.0;
1972*8af74909SZhong Yang 	}
1973*8af74909SZhong Yang 	// Convert byte positions to wchar_t positions
1974*8af74909SZhong Yang 	const size_t position = GetPositionInLayout(text, caretPosition);
1975*8af74909SZhong Yang 
1976*8af74909SZhong Yang 	// Translate text character offset to point x,y.
1977*8af74909SZhong Yang 	DWRITE_HIT_TEST_METRICS caretMetrics {};
1978*8af74909SZhong Yang 	Point pt;
1979*8af74909SZhong Yang 
1980*8af74909SZhong Yang 	textLayout->HitTestTextPosition(
1981*8af74909SZhong Yang 		static_cast<UINT32>(position),
1982*8af74909SZhong Yang 		false, // trailing if false, else leading edge
1983*8af74909SZhong Yang 		&pt.x,
1984*8af74909SZhong Yang 		&pt.y,
1985*8af74909SZhong Yang 		&caretMetrics
1986*8af74909SZhong Yang 	);
1987*8af74909SZhong Yang 
1988*8af74909SZhong Yang 	return pt.x;
1989*8af74909SZhong Yang }
1990*8af74909SZhong Yang 
1991*8af74909SZhong Yang // Find the selection range rectangles
1992*8af74909SZhong Yang 
FindRangeIntervals(size_t start,size_t end)1993*8af74909SZhong Yang std::vector<Interval> ScreenLineLayout::FindRangeIntervals(size_t start, size_t end) {
1994*8af74909SZhong Yang 	std::vector<Interval> ret;
1995*8af74909SZhong Yang 
1996*8af74909SZhong Yang 	if (!textLayout || (start == end)) {
1997*8af74909SZhong Yang 		return ret;
1998*8af74909SZhong Yang 	}
1999*8af74909SZhong Yang 
2000*8af74909SZhong Yang 	// Convert byte positions to wchar_t positions
2001*8af74909SZhong Yang 	const size_t startPos = GetPositionInLayout(text, start);
2002*8af74909SZhong Yang 	const size_t endPos = GetPositionInLayout(text, end);
2003*8af74909SZhong Yang 
2004*8af74909SZhong Yang 	// Find selection range length
2005*8af74909SZhong Yang 	const size_t rangeLength = (endPos > startPos) ? (endPos - startPos) : (startPos - endPos);
2006*8af74909SZhong Yang 
2007*8af74909SZhong Yang 	// Determine actual number of hit-test ranges
2008*8af74909SZhong Yang 	UINT32 actualHitTestCount = 0;
2009*8af74909SZhong Yang 
2010*8af74909SZhong Yang 	// First try with 2 elements and if more needed, allocate.
2011*8af74909SZhong Yang 	std::vector<DWRITE_HIT_TEST_METRICS> hitTestMetrics(2);
2012*8af74909SZhong Yang 	textLayout->HitTestTextRange(
2013*8af74909SZhong Yang 		static_cast<UINT32>(startPos),
2014*8af74909SZhong Yang 		static_cast<UINT32>(rangeLength),
2015*8af74909SZhong Yang 		0, // x
2016*8af74909SZhong Yang 		0, // y
2017*8af74909SZhong Yang 		hitTestMetrics.data(),
2018*8af74909SZhong Yang 		static_cast<UINT32>(hitTestMetrics.size()),
2019*8af74909SZhong Yang 		&actualHitTestCount
2020*8af74909SZhong Yang 	);
2021*8af74909SZhong Yang 
2022*8af74909SZhong Yang 	if (actualHitTestCount == 0) {
2023*8af74909SZhong Yang 		return ret;
2024*8af74909SZhong Yang 	}
2025*8af74909SZhong Yang 
2026*8af74909SZhong Yang 	if (hitTestMetrics.size() < actualHitTestCount) {
2027*8af74909SZhong Yang 		// Allocate enough room to return all hit-test metrics.
2028*8af74909SZhong Yang 		hitTestMetrics.resize(actualHitTestCount);
2029*8af74909SZhong Yang 		textLayout->HitTestTextRange(
2030*8af74909SZhong Yang 			static_cast<UINT32>(startPos),
2031*8af74909SZhong Yang 			static_cast<UINT32>(rangeLength),
2032*8af74909SZhong Yang 			0, // x
2033*8af74909SZhong Yang 			0, // y
2034*8af74909SZhong Yang 			hitTestMetrics.data(),
2035*8af74909SZhong Yang 			static_cast<UINT32>(hitTestMetrics.size()),
2036*8af74909SZhong Yang 			&actualHitTestCount
2037*8af74909SZhong Yang 		);
2038*8af74909SZhong Yang 	}
2039*8af74909SZhong Yang 
2040*8af74909SZhong Yang 	// Get the selection ranges behind the text.
2041*8af74909SZhong Yang 
2042*8af74909SZhong Yang 	for (size_t i = 0; i < actualHitTestCount; ++i) {
2043*8af74909SZhong Yang 		// Store selection rectangle
2044*8af74909SZhong Yang 		const DWRITE_HIT_TEST_METRICS &htm = hitTestMetrics[i];
2045*8af74909SZhong Yang 		Interval selectionInterval;
2046*8af74909SZhong Yang 
2047*8af74909SZhong Yang 		selectionInterval.left = htm.left;
2048*8af74909SZhong Yang 		selectionInterval.right = htm.left + htm.width;
2049*8af74909SZhong Yang 
2050*8af74909SZhong Yang 		ret.push_back(selectionInterval);
2051*8af74909SZhong Yang 	}
2052*8af74909SZhong Yang 
2053*8af74909SZhong Yang 	return ret;
2054*8af74909SZhong Yang }
2055*8af74909SZhong Yang 
Layout(const IScreenLine * screenLine)2056*8af74909SZhong Yang std::unique_ptr<IScreenLineLayout> SurfaceD2D::Layout(const IScreenLine *screenLine) {
2057*8af74909SZhong Yang 	return std::make_unique<ScreenLineLayout>(screenLine);
2058*8af74909SZhong Yang }
2059*8af74909SZhong Yang 
DrawTextCommon(PRectangle rc,const Font & font_,XYPOSITION ybase,std::string_view text,UINT fuOptions)2060*8af74909SZhong Yang void SurfaceD2D::DrawTextCommon(PRectangle rc, const Font &font_, XYPOSITION ybase, std::string_view text, UINT fuOptions) {
2061*8af74909SZhong Yang 	SetFont(font_);
2062*8af74909SZhong Yang 
2063*8af74909SZhong Yang 	// Use Unicode calls
2064*8af74909SZhong Yang 	const TextWide tbuf(text, unicodeMode, codePageText);
2065*8af74909SZhong Yang 	if (pRenderTarget && pTextFormat && pBrush) {
2066*8af74909SZhong Yang 		if (fuOptions & ETO_CLIPPED) {
2067*8af74909SZhong Yang 			const D2D1_RECT_F rcClip = RectangleFromPRectangle(rc);
2068*8af74909SZhong Yang 			pRenderTarget->PushAxisAlignedClip(rcClip, D2D1_ANTIALIAS_MODE_ALIASED);
2069*8af74909SZhong Yang 		}
2070*8af74909SZhong Yang 
2071*8af74909SZhong Yang 		// Explicitly creating a text layout appears a little faster
2072*8af74909SZhong Yang 		IDWriteTextLayout *pTextLayout = nullptr;
2073*8af74909SZhong Yang 		const HRESULT hr = pIDWriteFactory->CreateTextLayout(tbuf.buffer, tbuf.tlen, pTextFormat,
2074*8af74909SZhong Yang 				rc.Width(), rc.Height(), &pTextLayout);
2075*8af74909SZhong Yang 		if (SUCCEEDED(hr)) {
2076*8af74909SZhong Yang 			D2D1_POINT_2F origin = {rc.left, ybase-yAscent};
2077*8af74909SZhong Yang 			pRenderTarget->DrawTextLayout(origin, pTextLayout, pBrush, d2dDrawTextOptions);
2078*8af74909SZhong Yang 			ReleaseUnknown(pTextLayout);
2079*8af74909SZhong Yang 		}
2080*8af74909SZhong Yang 
2081*8af74909SZhong Yang 		if (fuOptions & ETO_CLIPPED) {
2082*8af74909SZhong Yang 			pRenderTarget->PopAxisAlignedClip();
2083*8af74909SZhong Yang 		}
2084*8af74909SZhong Yang 	}
2085*8af74909SZhong Yang }
2086*8af74909SZhong Yang 
DrawTextNoClip(PRectangle rc,Font & font_,XYPOSITION ybase,std::string_view text,ColourDesired fore,ColourDesired back)2087*8af74909SZhong Yang void SurfaceD2D::DrawTextNoClip(PRectangle rc, Font &font_, XYPOSITION ybase, std::string_view text,
2088*8af74909SZhong Yang 	ColourDesired fore, ColourDesired back) {
2089*8af74909SZhong Yang 	if (pRenderTarget) {
2090*8af74909SZhong Yang 		FillRectangle(rc, back);
2091*8af74909SZhong Yang 		D2DPenColour(fore);
2092*8af74909SZhong Yang 		DrawTextCommon(rc, font_, ybase, text, ETO_OPAQUE);
2093*8af74909SZhong Yang 	}
2094*8af74909SZhong Yang }
2095*8af74909SZhong Yang 
DrawTextClipped(PRectangle rc,Font & font_,XYPOSITION ybase,std::string_view text,ColourDesired fore,ColourDesired back)2096*8af74909SZhong Yang void SurfaceD2D::DrawTextClipped(PRectangle rc, Font &font_, XYPOSITION ybase, std::string_view text,
2097*8af74909SZhong Yang 	ColourDesired fore, ColourDesired back) {
2098*8af74909SZhong Yang 	if (pRenderTarget) {
2099*8af74909SZhong Yang 		FillRectangle(rc, back);
2100*8af74909SZhong Yang 		D2DPenColour(fore);
2101*8af74909SZhong Yang 		DrawTextCommon(rc, font_, ybase, text, ETO_OPAQUE | ETO_CLIPPED);
2102*8af74909SZhong Yang 	}
2103*8af74909SZhong Yang }
2104*8af74909SZhong Yang 
DrawTextTransparent(PRectangle rc,Font & font_,XYPOSITION ybase,std::string_view text,ColourDesired fore)2105*8af74909SZhong Yang void SurfaceD2D::DrawTextTransparent(PRectangle rc, Font &font_, XYPOSITION ybase, std::string_view text,
2106*8af74909SZhong Yang 	ColourDesired fore) {
2107*8af74909SZhong Yang 	// Avoid drawing spaces in transparent mode
2108*8af74909SZhong Yang 	for (const char ch : text) {
2109*8af74909SZhong Yang 		if (ch != ' ') {
2110*8af74909SZhong Yang 			if (pRenderTarget) {
2111*8af74909SZhong Yang 				D2DPenColour(fore);
2112*8af74909SZhong Yang 				DrawTextCommon(rc, font_, ybase, text, 0);
2113*8af74909SZhong Yang 			}
2114*8af74909SZhong Yang 			return;
2115*8af74909SZhong Yang 		}
2116*8af74909SZhong Yang 	}
2117*8af74909SZhong Yang }
2118*8af74909SZhong Yang 
WidthText(Font & font_,std::string_view text)2119*8af74909SZhong Yang XYPOSITION SurfaceD2D::WidthText(Font &font_, std::string_view text) {
2120*8af74909SZhong Yang 	FLOAT width = 1.0;
2121*8af74909SZhong Yang 	SetFont(font_);
2122*8af74909SZhong Yang 	const TextWide tbuf(text, unicodeMode, codePageText);
2123*8af74909SZhong Yang 	if (pIDWriteFactory && pTextFormat) {
2124*8af74909SZhong Yang 		// Create a layout
2125*8af74909SZhong Yang 		IDWriteTextLayout *pTextLayout = nullptr;
2126*8af74909SZhong Yang 		const HRESULT hr = pIDWriteFactory->CreateTextLayout(tbuf.buffer, tbuf.tlen, pTextFormat, 1000.0, 1000.0, &pTextLayout);
2127*8af74909SZhong Yang 		if (SUCCEEDED(hr) && pTextLayout) {
2128*8af74909SZhong Yang 			DWRITE_TEXT_METRICS textMetrics;
2129*8af74909SZhong Yang 			if (SUCCEEDED(pTextLayout->GetMetrics(&textMetrics)))
2130*8af74909SZhong Yang 				width = textMetrics.widthIncludingTrailingWhitespace;
2131*8af74909SZhong Yang 			ReleaseUnknown(pTextLayout);
2132*8af74909SZhong Yang 		}
2133*8af74909SZhong Yang 	}
2134*8af74909SZhong Yang 	return width;
2135*8af74909SZhong Yang }
2136*8af74909SZhong Yang 
MeasureWidths(Font & font_,std::string_view text,XYPOSITION * positions)2137*8af74909SZhong Yang void SurfaceD2D::MeasureWidths(Font &font_, std::string_view text, XYPOSITION *positions) {
2138*8af74909SZhong Yang 	SetFont(font_);
2139*8af74909SZhong Yang 	if (!pIDWriteFactory || !pTextFormat) {
2140*8af74909SZhong Yang 		// SetFont failed or no access to DirectWrite so give up.
2141*8af74909SZhong Yang 		return;
2142*8af74909SZhong Yang 	}
2143*8af74909SZhong Yang 	const TextWide tbuf(text, unicodeMode, codePageText);
2144*8af74909SZhong Yang 	TextPositions poses(tbuf.tlen);
2145*8af74909SZhong Yang 	// Initialize poses for safety.
2146*8af74909SZhong Yang 	std::fill(poses.buffer, poses.buffer + tbuf.tlen, 0.0f);
2147*8af74909SZhong Yang 	// Create a layout
2148*8af74909SZhong Yang 	IDWriteTextLayout *pTextLayout = nullptr;
2149*8af74909SZhong Yang 	const HRESULT hrCreate = pIDWriteFactory->CreateTextLayout(tbuf.buffer, tbuf.tlen, pTextFormat, 10000.0, 1000.0, &pTextLayout);
2150*8af74909SZhong Yang 	if (!SUCCEEDED(hrCreate) || !pTextLayout) {
2151*8af74909SZhong Yang 		return;
2152*8af74909SZhong Yang 	}
2153*8af74909SZhong Yang 	constexpr int clusters = stackBufferLength;
2154*8af74909SZhong Yang 	DWRITE_CLUSTER_METRICS clusterMetrics[clusters];
2155*8af74909SZhong Yang 	UINT32 count = 0;
2156*8af74909SZhong Yang 	const HRESULT hrGetCluster = pTextLayout->GetClusterMetrics(clusterMetrics, clusters, &count);
2157*8af74909SZhong Yang 	ReleaseUnknown(pTextLayout);
2158*8af74909SZhong Yang 	if (!SUCCEEDED(hrGetCluster)) {
2159*8af74909SZhong Yang 		return;
2160*8af74909SZhong Yang 	}
2161*8af74909SZhong Yang 	// A cluster may be more than one WCHAR, such as for "ffi" which is a ligature in the Candara font
2162*8af74909SZhong Yang 	FLOAT position = 0.0f;
2163*8af74909SZhong Yang 	int ti=0;
2164*8af74909SZhong Yang 	for (unsigned int ci=0; ci<count; ci++) {
2165*8af74909SZhong Yang 		for (unsigned int inCluster=0; inCluster<clusterMetrics[ci].length; inCluster++) {
2166*8af74909SZhong Yang 			poses.buffer[ti++] = position + clusterMetrics[ci].width * (inCluster + 1) / clusterMetrics[ci].length;
2167*8af74909SZhong Yang 		}
2168*8af74909SZhong Yang 		position += clusterMetrics[ci].width;
2169*8af74909SZhong Yang 	}
2170*8af74909SZhong Yang 	PLATFORM_ASSERT(ti == tbuf.tlen);
2171*8af74909SZhong Yang 	if (unicodeMode) {
2172*8af74909SZhong Yang 		// Map the widths given for UTF-16 characters back onto the UTF-8 input string
2173*8af74909SZhong Yang 		int ui=0;
2174*8af74909SZhong Yang 		size_t i=0;
2175*8af74909SZhong Yang 		while (ui<tbuf.tlen) {
2176*8af74909SZhong Yang 			const unsigned char uch = text[i];
2177*8af74909SZhong Yang 			const unsigned int byteCount = UTF8BytesOfLead[uch];
2178*8af74909SZhong Yang 			if (byteCount == 4) {	// Non-BMP
2179*8af74909SZhong Yang 				ui++;
2180*8af74909SZhong Yang 			}
2181*8af74909SZhong Yang 			for (unsigned int bytePos=0; (bytePos<byteCount) && (i<text.length()) && (ui<tbuf.tlen); bytePos++) {
2182*8af74909SZhong Yang 				positions[i++] = poses.buffer[ui];
2183*8af74909SZhong Yang 			}
2184*8af74909SZhong Yang 			ui++;
2185*8af74909SZhong Yang 		}
2186*8af74909SZhong Yang 		XYPOSITION lastPos = 0.0f;
2187*8af74909SZhong Yang 		if (i > 0)
2188*8af74909SZhong Yang 			lastPos = positions[i-1];
2189*8af74909SZhong Yang 		while (i<text.length()) {
2190*8af74909SZhong Yang 			positions[i++] = lastPos;
2191*8af74909SZhong Yang 		}
2192*8af74909SZhong Yang 	} else if (!IsDBCSCodePage(codePageText)) {
2193*8af74909SZhong Yang 
2194*8af74909SZhong Yang 		// One char per position
2195*8af74909SZhong Yang 		PLATFORM_ASSERT(text.length() == static_cast<size_t>(tbuf.tlen));
2196*8af74909SZhong Yang 		for (int kk=0; kk<tbuf.tlen; kk++) {
2197*8af74909SZhong Yang 			positions[kk] = poses.buffer[kk];
2198*8af74909SZhong Yang 		}
2199*8af74909SZhong Yang 
2200*8af74909SZhong Yang 	} else {
2201*8af74909SZhong Yang 
2202*8af74909SZhong Yang 		// May be one or two bytes per position
2203*8af74909SZhong Yang 		int ui = 0;
2204*8af74909SZhong Yang 		for (size_t i=0; i<text.length() && ui<tbuf.tlen;) {
2205*8af74909SZhong Yang 			positions[i] = poses.buffer[ui];
2206*8af74909SZhong Yang 			if (DBCSIsLeadByte(codePageText, text[i])) {
2207*8af74909SZhong Yang 				positions[i+1] = poses.buffer[ui];
2208*8af74909SZhong Yang 				i += 2;
2209*8af74909SZhong Yang 			} else {
2210*8af74909SZhong Yang 				i++;
2211*8af74909SZhong Yang 			}
2212*8af74909SZhong Yang 
2213*8af74909SZhong Yang 			ui++;
2214*8af74909SZhong Yang 		}
2215*8af74909SZhong Yang 	}
2216*8af74909SZhong Yang }
2217*8af74909SZhong Yang 
Ascent(Font & font_)2218*8af74909SZhong Yang XYPOSITION SurfaceD2D::Ascent(Font &font_) {
2219*8af74909SZhong Yang 	SetFont(font_);
2220*8af74909SZhong Yang 	return std::ceil(yAscent);
2221*8af74909SZhong Yang }
2222*8af74909SZhong Yang 
Descent(Font & font_)2223*8af74909SZhong Yang XYPOSITION SurfaceD2D::Descent(Font &font_) {
2224*8af74909SZhong Yang 	SetFont(font_);
2225*8af74909SZhong Yang 	return std::ceil(yDescent);
2226*8af74909SZhong Yang }
2227*8af74909SZhong Yang 
InternalLeading(Font & font_)2228*8af74909SZhong Yang XYPOSITION SurfaceD2D::InternalLeading(Font &font_) {
2229*8af74909SZhong Yang 	SetFont(font_);
2230*8af74909SZhong Yang 	return std::floor(yInternalLeading);
2231*8af74909SZhong Yang }
2232*8af74909SZhong Yang 
Height(Font & font_)2233*8af74909SZhong Yang XYPOSITION SurfaceD2D::Height(Font &font_) {
2234*8af74909SZhong Yang 	return Ascent(font_) + Descent(font_);
2235*8af74909SZhong Yang }
2236*8af74909SZhong Yang 
AverageCharWidth(Font & font_)2237*8af74909SZhong Yang XYPOSITION SurfaceD2D::AverageCharWidth(Font &font_) {
2238*8af74909SZhong Yang 	FLOAT width = 1.0;
2239*8af74909SZhong Yang 	SetFont(font_);
2240*8af74909SZhong Yang 	if (pIDWriteFactory && pTextFormat) {
2241*8af74909SZhong Yang 		// Create a layout
2242*8af74909SZhong Yang 		IDWriteTextLayout *pTextLayout = nullptr;
2243*8af74909SZhong Yang 		const WCHAR wszAllAlpha[] = L"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
2244*8af74909SZhong Yang 		const size_t lenAllAlpha = wcslen(wszAllAlpha);
2245*8af74909SZhong Yang 		const HRESULT hr = pIDWriteFactory->CreateTextLayout(wszAllAlpha, static_cast<UINT32>(lenAllAlpha),
2246*8af74909SZhong Yang 			pTextFormat, 1000.0, 1000.0, &pTextLayout);
2247*8af74909SZhong Yang 		if (SUCCEEDED(hr) && pTextLayout) {
2248*8af74909SZhong Yang 			DWRITE_TEXT_METRICS textMetrics;
2249*8af74909SZhong Yang 			if (SUCCEEDED(pTextLayout->GetMetrics(&textMetrics)))
2250*8af74909SZhong Yang 				width = textMetrics.width / lenAllAlpha;
2251*8af74909SZhong Yang 			ReleaseUnknown(pTextLayout);
2252*8af74909SZhong Yang 		}
2253*8af74909SZhong Yang 	}
2254*8af74909SZhong Yang 	return width;
2255*8af74909SZhong Yang }
2256*8af74909SZhong Yang 
SetClip(PRectangle rc)2257*8af74909SZhong Yang void SurfaceD2D::SetClip(PRectangle rc) {
2258*8af74909SZhong Yang 	if (pRenderTarget) {
2259*8af74909SZhong Yang 		const D2D1_RECT_F rcClip = RectangleFromPRectangle(rc);
2260*8af74909SZhong Yang 		pRenderTarget->PushAxisAlignedClip(rcClip, D2D1_ANTIALIAS_MODE_ALIASED);
2261*8af74909SZhong Yang 		clipsActive++;
2262*8af74909SZhong Yang 	}
2263*8af74909SZhong Yang }
2264*8af74909SZhong Yang 
FlushCachedState()2265*8af74909SZhong Yang void SurfaceD2D::FlushCachedState() {
2266*8af74909SZhong Yang }
2267*8af74909SZhong Yang 
SetUnicodeMode(bool unicodeMode_)2268*8af74909SZhong Yang void SurfaceD2D::SetUnicodeMode(bool unicodeMode_) {
2269*8af74909SZhong Yang 	unicodeMode=unicodeMode_;
2270*8af74909SZhong Yang }
2271*8af74909SZhong Yang 
SetDBCSMode(int codePage_)2272*8af74909SZhong Yang void SurfaceD2D::SetDBCSMode(int codePage_) {
2273*8af74909SZhong Yang 	// No action on window as automatically handled by system.
2274*8af74909SZhong Yang 	codePage = codePage_;
2275*8af74909SZhong Yang }
2276*8af74909SZhong Yang 
SetBidiR2L(bool)2277*8af74909SZhong Yang void SurfaceD2D::SetBidiR2L(bool) {
2278*8af74909SZhong Yang }
2279*8af74909SZhong Yang 
2280*8af74909SZhong Yang #endif
2281*8af74909SZhong Yang 
Allocate(int technology)2282*8af74909SZhong Yang Surface *Surface::Allocate(int technology) {
2283*8af74909SZhong Yang #if defined(USE_D2D)
2284*8af74909SZhong Yang 	if (technology == SCWIN_TECH_GDI)
2285*8af74909SZhong Yang 		return new SurfaceGDI;
2286*8af74909SZhong Yang 	else
2287*8af74909SZhong Yang 		return new SurfaceD2D;
2288*8af74909SZhong Yang #else
2289*8af74909SZhong Yang 	return new SurfaceGDI;
2290*8af74909SZhong Yang #endif
2291*8af74909SZhong Yang }
2292*8af74909SZhong Yang 
~Window()2293*8af74909SZhong Yang Window::~Window() {
2294*8af74909SZhong Yang }
2295*8af74909SZhong Yang 
Destroy()2296*8af74909SZhong Yang void Window::Destroy() {
2297*8af74909SZhong Yang 	if (wid)
2298*8af74909SZhong Yang 		::DestroyWindow(HwndFromWindowID(wid));
2299*8af74909SZhong Yang 	wid = nullptr;
2300*8af74909SZhong Yang }
2301*8af74909SZhong Yang 
GetPosition() const2302*8af74909SZhong Yang PRectangle Window::GetPosition() const {
2303*8af74909SZhong Yang 	RECT rc;
2304*8af74909SZhong Yang 	::GetWindowRect(HwndFromWindowID(wid), &rc);
2305*8af74909SZhong Yang 	return PRectangle::FromInts(rc.left, rc.top, rc.right, rc.bottom);
2306*8af74909SZhong Yang }
2307*8af74909SZhong Yang 
SetPosition(PRectangle rc)2308*8af74909SZhong Yang void Window::SetPosition(PRectangle rc) {
2309*8af74909SZhong Yang 	::SetWindowPos(HwndFromWindowID(wid),
2310*8af74909SZhong Yang 		0, static_cast<int>(rc.left), static_cast<int>(rc.top),
2311*8af74909SZhong Yang 		static_cast<int>(rc.Width()), static_cast<int>(rc.Height()), SWP_NOZORDER | SWP_NOACTIVATE);
2312*8af74909SZhong Yang }
2313*8af74909SZhong Yang 
2314*8af74909SZhong Yang namespace {
2315*8af74909SZhong Yang 
RectFromMonitor(HMONITOR hMonitor)2316*8af74909SZhong Yang static RECT RectFromMonitor(HMONITOR hMonitor) noexcept {
2317*8af74909SZhong Yang 	MONITORINFO mi = {};
2318*8af74909SZhong Yang 	mi.cbSize = sizeof(mi);
2319*8af74909SZhong Yang 	if (GetMonitorInfo(hMonitor, &mi)) {
2320*8af74909SZhong Yang 		return mi.rcWork;
2321*8af74909SZhong Yang 	}
2322*8af74909SZhong Yang 	RECT rc = {0, 0, 0, 0};
2323*8af74909SZhong Yang 	if (::SystemParametersInfoA(SPI_GETWORKAREA, 0, &rc, 0) == 0) {
2324*8af74909SZhong Yang 		rc.left = 0;
2325*8af74909SZhong Yang 		rc.top = 0;
2326*8af74909SZhong Yang 		rc.right = 0;
2327*8af74909SZhong Yang 		rc.bottom = 0;
2328*8af74909SZhong Yang 	}
2329*8af74909SZhong Yang 	return rc;
2330*8af74909SZhong Yang }
2331*8af74909SZhong Yang 
2332*8af74909SZhong Yang }
2333*8af74909SZhong Yang 
SetPositionRelative(PRectangle rc,const Window * relativeTo)2334*8af74909SZhong Yang void Window::SetPositionRelative(PRectangle rc, const Window *relativeTo) {
2335*8af74909SZhong Yang 	const DWORD style = GetWindowStyle(HwndFromWindowID(wid));
2336*8af74909SZhong Yang 	if (style & WS_POPUP) {
2337*8af74909SZhong Yang 		POINT ptOther = {0, 0};
2338*8af74909SZhong Yang 		::ClientToScreen(HwndFromWindow(*relativeTo), &ptOther);
2339*8af74909SZhong Yang 		rc.Move(static_cast<XYPOSITION>(ptOther.x), static_cast<XYPOSITION>(ptOther.y));
2340*8af74909SZhong Yang 
2341*8af74909SZhong Yang 		const RECT rcMonitor = RectFromPRectangle(rc);
2342*8af74909SZhong Yang 
2343*8af74909SZhong Yang 		HMONITOR hMonitor = MonitorFromRect(&rcMonitor, MONITOR_DEFAULTTONEAREST);
2344*8af74909SZhong Yang 		// If hMonitor is NULL, that's just the main screen anyways.
2345*8af74909SZhong Yang 		const RECT rcWork = RectFromMonitor(hMonitor);
2346*8af74909SZhong Yang 
2347*8af74909SZhong Yang 		if (rcWork.left < rcWork.right) {
2348*8af74909SZhong Yang 			// Now clamp our desired rectangle to fit inside the work area
2349*8af74909SZhong Yang 			// This way, the menu will fit wholly on one screen. An improvement even
2350*8af74909SZhong Yang 			// if you don't have a second monitor on the left... Menu's appears half on
2351*8af74909SZhong Yang 			// one screen and half on the other are just U.G.L.Y.!
2352*8af74909SZhong Yang 			if (rc.right > rcWork.right)
2353*8af74909SZhong Yang 				rc.Move(rcWork.right - rc.right, 0);
2354*8af74909SZhong Yang 			if (rc.bottom > rcWork.bottom)
2355*8af74909SZhong Yang 				rc.Move(0, rcWork.bottom - rc.bottom);
2356*8af74909SZhong Yang 			if (rc.left < rcWork.left)
2357*8af74909SZhong Yang 				rc.Move(rcWork.left - rc.left, 0);
2358*8af74909SZhong Yang 			if (rc.top < rcWork.top)
2359*8af74909SZhong Yang 				rc.Move(0, rcWork.top - rc.top);
2360*8af74909SZhong Yang 		}
2361*8af74909SZhong Yang 	}
2362*8af74909SZhong Yang 	SetPosition(rc);
2363*8af74909SZhong Yang }
2364*8af74909SZhong Yang 
GetClientPosition() const2365*8af74909SZhong Yang PRectangle Window::GetClientPosition() const {
2366*8af74909SZhong Yang 	RECT rc={0,0,0,0};
2367*8af74909SZhong Yang 	if (wid)
2368*8af74909SZhong Yang 		::GetClientRect(HwndFromWindowID(wid), &rc);
2369*8af74909SZhong Yang 	return PRectangle::FromInts(rc.left, rc.top, rc.right, rc.bottom);
2370*8af74909SZhong Yang }
2371*8af74909SZhong Yang 
Show(bool show)2372*8af74909SZhong Yang void Window::Show(bool show) {
2373*8af74909SZhong Yang 	if (show)
2374*8af74909SZhong Yang 		::ShowWindow(HwndFromWindowID(wid), SW_SHOWNOACTIVATE);
2375*8af74909SZhong Yang 	else
2376*8af74909SZhong Yang 		::ShowWindow(HwndFromWindowID(wid), SW_HIDE);
2377*8af74909SZhong Yang }
2378*8af74909SZhong Yang 
InvalidateAll()2379*8af74909SZhong Yang void Window::InvalidateAll() {
2380*8af74909SZhong Yang 	::InvalidateRect(HwndFromWindowID(wid), nullptr, FALSE);
2381*8af74909SZhong Yang }
2382*8af74909SZhong Yang 
InvalidateRectangle(PRectangle rc)2383*8af74909SZhong Yang void Window::InvalidateRectangle(PRectangle rc) {
2384*8af74909SZhong Yang 	const RECT rcw = RectFromPRectangle(rc);
2385*8af74909SZhong Yang 	::InvalidateRect(HwndFromWindowID(wid), &rcw, FALSE);
2386*8af74909SZhong Yang }
2387*8af74909SZhong Yang 
SetFont(Font & font)2388*8af74909SZhong Yang void Window::SetFont(Font &font) {
2389*8af74909SZhong Yang 	SetWindowFont(HwndFromWindowID(wid), font.GetID(), 0);
2390*8af74909SZhong Yang }
2391*8af74909SZhong Yang 
2392*8af74909SZhong Yang namespace {
2393*8af74909SZhong Yang 
FlipBitmap(HBITMAP bitmap,int width,int height)2394*8af74909SZhong Yang void FlipBitmap(HBITMAP bitmap, int width, int height) noexcept {
2395*8af74909SZhong Yang 	HDC hdc = ::CreateCompatibleDC({});
2396*8af74909SZhong Yang 	if (hdc) {
2397*8af74909SZhong Yang 		HBITMAP prevBmp = SelectBitmap(hdc, bitmap);
2398*8af74909SZhong Yang 		::StretchBlt(hdc, width - 1, 0, -width, height, hdc, 0, 0, width, height, SRCCOPY);
2399*8af74909SZhong Yang 		SelectBitmap(hdc, prevBmp);
2400*8af74909SZhong Yang 		::DeleteDC(hdc);
2401*8af74909SZhong Yang 	}
2402*8af74909SZhong Yang }
2403*8af74909SZhong Yang 
2404*8af74909SZhong Yang }
2405*8af74909SZhong Yang 
LoadReverseArrowCursor(UINT dpi)2406*8af74909SZhong Yang HCURSOR LoadReverseArrowCursor(UINT dpi) noexcept {
2407*8af74909SZhong Yang 	HCURSOR reverseArrowCursor {};
2408*8af74909SZhong Yang 
2409*8af74909SZhong Yang 	bool created = false;
2410*8af74909SZhong Yang 	HCURSOR cursor = ::LoadCursor({}, IDC_ARROW);
2411*8af74909SZhong Yang 
2412*8af74909SZhong Yang 	if (dpi != uSystemDPI) {
2413*8af74909SZhong Yang 		const int width = SystemMetricsForDpi(SM_CXCURSOR, dpi);
2414*8af74909SZhong Yang 		const int height = SystemMetricsForDpi(SM_CYCURSOR, dpi);
2415*8af74909SZhong Yang 		HCURSOR copy = static_cast<HCURSOR>(::CopyImage(cursor, IMAGE_CURSOR, width, height, LR_COPYFROMRESOURCE | LR_COPYRETURNORG));
2416*8af74909SZhong Yang 		if (copy) {
2417*8af74909SZhong Yang 			created = copy != cursor;
2418*8af74909SZhong Yang 			cursor = copy;
2419*8af74909SZhong Yang 		}
2420*8af74909SZhong Yang 	}
2421*8af74909SZhong Yang 
2422*8af74909SZhong Yang 	ICONINFO info;
2423*8af74909SZhong Yang 	if (::GetIconInfo(cursor, &info)) {
2424*8af74909SZhong Yang 		BITMAP bmp;
2425*8af74909SZhong Yang 		if (::GetObject(info.hbmMask, sizeof(bmp), &bmp)) {
2426*8af74909SZhong Yang 			FlipBitmap(info.hbmMask, bmp.bmWidth, bmp.bmHeight);
2427*8af74909SZhong Yang 			if (info.hbmColor)
2428*8af74909SZhong Yang 				FlipBitmap(info.hbmColor, bmp.bmWidth, bmp.bmHeight);
2429*8af74909SZhong Yang 			info.xHotspot = bmp.bmWidth - 1 - info.xHotspot;
2430*8af74909SZhong Yang 
2431*8af74909SZhong Yang 			reverseArrowCursor = ::CreateIconIndirect(&info);
2432*8af74909SZhong Yang 		}
2433*8af74909SZhong Yang 
2434*8af74909SZhong Yang 		::DeleteObject(info.hbmMask);
2435*8af74909SZhong Yang 		if (info.hbmColor)
2436*8af74909SZhong Yang 			::DeleteObject(info.hbmColor);
2437*8af74909SZhong Yang 	}
2438*8af74909SZhong Yang 
2439*8af74909SZhong Yang 	if (created) {
2440*8af74909SZhong Yang 		::DestroyCursor(cursor);
2441*8af74909SZhong Yang 	}
2442*8af74909SZhong Yang 	return reverseArrowCursor;
2443*8af74909SZhong Yang }
2444*8af74909SZhong Yang 
SetCursor(Cursor curs)2445*8af74909SZhong Yang void Window::SetCursor(Cursor curs) {
2446*8af74909SZhong Yang 	switch (curs) {
2447*8af74909SZhong Yang 	case cursorText:
2448*8af74909SZhong Yang 		::SetCursor(::LoadCursor(NULL,IDC_IBEAM));
2449*8af74909SZhong Yang 		break;
2450*8af74909SZhong Yang 	case cursorUp:
2451*8af74909SZhong Yang 		::SetCursor(::LoadCursor(NULL,IDC_UPARROW));
2452*8af74909SZhong Yang 		break;
2453*8af74909SZhong Yang 	case cursorWait:
2454*8af74909SZhong Yang 		::SetCursor(::LoadCursor(NULL,IDC_WAIT));
2455*8af74909SZhong Yang 		break;
2456*8af74909SZhong Yang 	case cursorHoriz:
2457*8af74909SZhong Yang 		::SetCursor(::LoadCursor(NULL,IDC_SIZEWE));
2458*8af74909SZhong Yang 		break;
2459*8af74909SZhong Yang 	case cursorVert:
2460*8af74909SZhong Yang 		::SetCursor(::LoadCursor(NULL,IDC_SIZENS));
2461*8af74909SZhong Yang 		break;
2462*8af74909SZhong Yang 	case cursorHand:
2463*8af74909SZhong Yang 		::SetCursor(::LoadCursor(NULL,IDC_HAND));
2464*8af74909SZhong Yang 		break;
2465*8af74909SZhong Yang 	case cursorReverseArrow:
2466*8af74909SZhong Yang 	case cursorArrow:
2467*8af74909SZhong Yang 	case cursorInvalid:	// Should not occur, but just in case.
2468*8af74909SZhong Yang 		::SetCursor(::LoadCursor(NULL,IDC_ARROW));
2469*8af74909SZhong Yang 		break;
2470*8af74909SZhong Yang 	}
2471*8af74909SZhong Yang }
2472*8af74909SZhong Yang 
2473*8af74909SZhong Yang /* Returns rectangle of monitor pt is on, both rect and pt are in Window's
2474*8af74909SZhong Yang    coordinates */
GetMonitorRect(Point pt)2475*8af74909SZhong Yang PRectangle Window::GetMonitorRect(Point pt) {
2476*8af74909SZhong Yang 	const PRectangle rcPosition = GetPosition();
2477*8af74909SZhong Yang 	POINT ptDesktop = {static_cast<LONG>(pt.x + rcPosition.left),
2478*8af74909SZhong Yang 		static_cast<LONG>(pt.y + rcPosition.top)};
2479*8af74909SZhong Yang 	HMONITOR hMonitor = MonitorFromPoint(ptDesktop, MONITOR_DEFAULTTONEAREST);
2480*8af74909SZhong Yang 
2481*8af74909SZhong Yang 	const RECT rcWork = RectFromMonitor(hMonitor);
2482*8af74909SZhong Yang 	if (rcWork.left < rcWork.right) {
2483*8af74909SZhong Yang 		PRectangle rcMonitor(
2484*8af74909SZhong Yang 			rcWork.left - rcPosition.left,
2485*8af74909SZhong Yang 			rcWork.top - rcPosition.top,
2486*8af74909SZhong Yang 			rcWork.right - rcPosition.left,
2487*8af74909SZhong Yang 			rcWork.bottom - rcPosition.top);
2488*8af74909SZhong Yang 		return rcMonitor;
2489*8af74909SZhong Yang 	} else {
2490*8af74909SZhong Yang 		return PRectangle();
2491*8af74909SZhong Yang 	}
2492*8af74909SZhong Yang }
2493*8af74909SZhong Yang 
2494*8af74909SZhong Yang struct ListItemData {
2495*8af74909SZhong Yang 	const char *text;
2496*8af74909SZhong Yang 	int pixId;
2497*8af74909SZhong Yang };
2498*8af74909SZhong Yang 
2499*8af74909SZhong Yang class LineToItem {
2500*8af74909SZhong Yang 	std::vector<char> words;
2501*8af74909SZhong Yang 
2502*8af74909SZhong Yang 	std::vector<ListItemData> data;
2503*8af74909SZhong Yang 
2504*8af74909SZhong Yang public:
Clear()2505*8af74909SZhong Yang 	void Clear() noexcept {
2506*8af74909SZhong Yang 		words.clear();
2507*8af74909SZhong Yang 		data.clear();
2508*8af74909SZhong Yang 	}
2509*8af74909SZhong Yang 
Get(size_t index) const2510*8af74909SZhong Yang 	ListItemData Get(size_t index) const noexcept {
2511*8af74909SZhong Yang 		if (index < data.size()) {
2512*8af74909SZhong Yang 			return data[index];
2513*8af74909SZhong Yang 		} else {
2514*8af74909SZhong Yang 			ListItemData missing = {"", -1};
2515*8af74909SZhong Yang 			return missing;
2516*8af74909SZhong Yang 		}
2517*8af74909SZhong Yang 	}
Count() const2518*8af74909SZhong Yang 	int Count() const noexcept {
2519*8af74909SZhong Yang 		return static_cast<int>(data.size());
2520*8af74909SZhong Yang 	}
2521*8af74909SZhong Yang 
AllocItem(const char * text,int pixId)2522*8af74909SZhong Yang 	void AllocItem(const char *text, int pixId) {
2523*8af74909SZhong Yang 		ListItemData lid = { text, pixId };
2524*8af74909SZhong Yang 		data.push_back(lid);
2525*8af74909SZhong Yang 	}
2526*8af74909SZhong Yang 
SetWords(const char * s)2527*8af74909SZhong Yang 	char *SetWords(const char *s) {
2528*8af74909SZhong Yang 		words = std::vector<char>(s, s+strlen(s)+1);
2529*8af74909SZhong Yang 		return &words[0];
2530*8af74909SZhong Yang 	}
2531*8af74909SZhong Yang };
2532*8af74909SZhong Yang 
2533*8af74909SZhong Yang const TCHAR ListBoxX_ClassName[] = TEXT("ListBoxX");
2534*8af74909SZhong Yang 
ListBox()2535*8af74909SZhong Yang ListBox::ListBox() noexcept {
2536*8af74909SZhong Yang }
2537*8af74909SZhong Yang 
~ListBox()2538*8af74909SZhong Yang ListBox::~ListBox() {
2539*8af74909SZhong Yang }
2540*8af74909SZhong Yang 
2541*8af74909SZhong Yang class ListBoxX : public ListBox {
2542*8af74909SZhong Yang 	int lineHeight;
2543*8af74909SZhong Yang 	FontID fontCopy;
2544*8af74909SZhong Yang 	int technology;
2545*8af74909SZhong Yang 	RGBAImageSet images;
2546*8af74909SZhong Yang 	LineToItem lti;
2547*8af74909SZhong Yang 	HWND lb;
2548*8af74909SZhong Yang 	bool unicodeMode;
2549*8af74909SZhong Yang 	int desiredVisibleRows;
2550*8af74909SZhong Yang 	unsigned int maxItemCharacters;
2551*8af74909SZhong Yang 	unsigned int aveCharWidth;
2552*8af74909SZhong Yang 	Window *parent;
2553*8af74909SZhong Yang 	int ctrlID;
2554*8af74909SZhong Yang 	UINT dpi;
2555*8af74909SZhong Yang 	IListBoxDelegate *delegate;
2556*8af74909SZhong Yang 	const char *widestItem;
2557*8af74909SZhong Yang 	unsigned int maxCharWidth;
2558*8af74909SZhong Yang 	WPARAM resizeHit;
2559*8af74909SZhong Yang 	PRectangle rcPreSize;
2560*8af74909SZhong Yang 	Point dragOffset;
2561*8af74909SZhong Yang 	Point location;	// Caret location at which the list is opened
2562*8af74909SZhong Yang 	int wheelDelta; // mouse wheel residue
2563*8af74909SZhong Yang 
2564*8af74909SZhong Yang 	HWND GetHWND() const noexcept;
2565*8af74909SZhong Yang 	void AppendListItem(const char *text, const char *numword);
2566*8af74909SZhong Yang 	static void AdjustWindowRect(PRectangle *rc, UINT dpi) noexcept;
2567*8af74909SZhong Yang 	int ItemHeight() const;
2568*8af74909SZhong Yang 	int MinClientWidth() const noexcept;
2569*8af74909SZhong Yang 	int TextOffset() const;
2570*8af74909SZhong Yang 	POINT GetClientExtent() const noexcept;
2571*8af74909SZhong Yang 	POINT MinTrackSize() const;
2572*8af74909SZhong Yang 	POINT MaxTrackSize() const;
2573*8af74909SZhong Yang 	void SetRedraw(bool on) noexcept;
2574*8af74909SZhong Yang 	void OnDoubleClick();
2575*8af74909SZhong Yang 	void OnSelChange();
2576*8af74909SZhong Yang 	void ResizeToCursor();
2577*8af74909SZhong Yang 	void StartResize(WPARAM);
2578*8af74909SZhong Yang 	LRESULT NcHitTest(WPARAM, LPARAM) const;
2579*8af74909SZhong Yang 	void CentreItem(int n);
2580*8af74909SZhong Yang 	void Paint(HDC) noexcept;
2581*8af74909SZhong Yang 	static LRESULT PASCAL ControlWndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam);
2582*8af74909SZhong Yang 
2583*8af74909SZhong Yang 	static constexpr Point ItemInset {0, 0};	// Padding around whole item
2584*8af74909SZhong Yang 	static constexpr Point TextInset {2, 0};	// Padding around text
2585*8af74909SZhong Yang 	static constexpr Point ImageInset {1, 0};	// Padding around image
2586*8af74909SZhong Yang 
2587*8af74909SZhong Yang public:
ListBoxX()2588*8af74909SZhong Yang 	ListBoxX() : lineHeight(10), fontCopy{}, technology(0), lb{}, unicodeMode(false),
2589*8af74909SZhong Yang 		desiredVisibleRows(9), maxItemCharacters(0), aveCharWidth(8),
2590*8af74909SZhong Yang 		parent(nullptr), ctrlID(0), dpi(USER_DEFAULT_SCREEN_DPI),
2591*8af74909SZhong Yang 		delegate(nullptr),
2592*8af74909SZhong Yang 		widestItem(nullptr), maxCharWidth(1), resizeHit(0), wheelDelta(0) {
2593*8af74909SZhong Yang 	}
~ListBoxX()2594*8af74909SZhong Yang 	~ListBoxX() override {
2595*8af74909SZhong Yang 		if (fontCopy) {
2596*8af74909SZhong Yang 			::DeleteObject(fontCopy);
2597*8af74909SZhong Yang 			fontCopy = 0;
2598*8af74909SZhong Yang 		}
2599*8af74909SZhong Yang 	}
2600*8af74909SZhong Yang 	void SetFont(Font &font) override;
2601*8af74909SZhong Yang 	void Create(Window &parent_, int ctrlID_, Point location_, int lineHeight_, bool unicodeMode_, int technology_) override;
2602*8af74909SZhong Yang 	void SetAverageCharWidth(int width) override;
2603*8af74909SZhong Yang 	void SetVisibleRows(int rows) override;
2604*8af74909SZhong Yang 	int GetVisibleRows() const override;
2605*8af74909SZhong Yang 	PRectangle GetDesiredRect() override;
2606*8af74909SZhong Yang 	int CaretFromEdge() override;
2607*8af74909SZhong Yang 	void Clear() override;
2608*8af74909SZhong Yang 	void Append(char *s, int type = -1) override;
2609*8af74909SZhong Yang 	int Length() override;
2610*8af74909SZhong Yang 	void Select(int n) override;
2611*8af74909SZhong Yang 	int GetSelection() override;
2612*8af74909SZhong Yang 	int Find(const char *prefix) override;
2613*8af74909SZhong Yang 	void GetValue(int n, char *value, int len) override;
2614*8af74909SZhong Yang 	void RegisterImage(int type, const char *xpm_data) override;
2615*8af74909SZhong Yang 	void RegisterRGBAImage(int type, int width, int height, const unsigned char *pixelsImage) override;
2616*8af74909SZhong Yang 	void ClearRegisteredImages() override;
2617*8af74909SZhong Yang 	void SetDelegate(IListBoxDelegate *lbDelegate) override;
2618*8af74909SZhong Yang 	void SetList(const char *list, char separator, char typesep) override;
2619*8af74909SZhong Yang 	void Draw(DRAWITEMSTRUCT *pDrawItem);
2620*8af74909SZhong Yang 	LRESULT WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam);
2621*8af74909SZhong Yang 	static LRESULT PASCAL StaticWndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam);
2622*8af74909SZhong Yang };
2623*8af74909SZhong Yang 
Allocate()2624*8af74909SZhong Yang ListBox *ListBox::Allocate() {
2625*8af74909SZhong Yang 	ListBoxX *lb = new ListBoxX();
2626*8af74909SZhong Yang 	return lb;
2627*8af74909SZhong Yang }
2628*8af74909SZhong Yang 
Create(Window & parent_,int ctrlID_,Point location_,int lineHeight_,bool unicodeMode_,int technology_)2629*8af74909SZhong Yang void ListBoxX::Create(Window &parent_, int ctrlID_, Point location_, int lineHeight_, bool unicodeMode_, int technology_) {
2630*8af74909SZhong Yang 	parent = &parent_;
2631*8af74909SZhong Yang 	ctrlID = ctrlID_;
2632*8af74909SZhong Yang 	location = location_;
2633*8af74909SZhong Yang 	lineHeight = lineHeight_;
2634*8af74909SZhong Yang 	unicodeMode = unicodeMode_;
2635*8af74909SZhong Yang 	technology = technology_;
2636*8af74909SZhong Yang 	HWND hwndParent = HwndFromWindow(*parent);
2637*8af74909SZhong Yang 	HINSTANCE hinstanceParent = GetWindowInstance(hwndParent);
2638*8af74909SZhong Yang 	// Window created as popup so not clipped within parent client area
2639*8af74909SZhong Yang 	wid = ::CreateWindowEx(
2640*8af74909SZhong Yang 		WS_EX_WINDOWEDGE, ListBoxX_ClassName, TEXT(""),
2641*8af74909SZhong Yang 		WS_POPUP | WS_THICKFRAME,
2642*8af74909SZhong Yang 		100,100, 150,80, hwndParent,
2643*8af74909SZhong Yang 		NULL,
2644*8af74909SZhong Yang 		hinstanceParent,
2645*8af74909SZhong Yang 		this);
2646*8af74909SZhong Yang 
2647*8af74909SZhong Yang 	dpi = DpiForWindow(hwndParent);
2648*8af74909SZhong Yang 	POINT locationw = POINTFromPoint(location);
2649*8af74909SZhong Yang 	::MapWindowPoints(hwndParent, NULL, &locationw, 1);
2650*8af74909SZhong Yang 	location = PointFromPOINT(locationw);
2651*8af74909SZhong Yang }
2652*8af74909SZhong Yang 
SetFont(Font & font)2653*8af74909SZhong Yang void ListBoxX::SetFont(Font &font) {
2654*8af74909SZhong Yang 	if (font.GetID()) {
2655*8af74909SZhong Yang 		if (fontCopy) {
2656*8af74909SZhong Yang 			::DeleteObject(fontCopy);
2657*8af74909SZhong Yang 			fontCopy = 0;
2658*8af74909SZhong Yang 		}
2659*8af74909SZhong Yang 		FormatAndMetrics *pfm = static_cast<FormatAndMetrics *>(font.GetID());
2660*8af74909SZhong Yang 		fontCopy = pfm->HFont();
2661*8af74909SZhong Yang 		SetWindowFont(lb, fontCopy, 0);
2662*8af74909SZhong Yang 	}
2663*8af74909SZhong Yang }
2664*8af74909SZhong Yang 
SetAverageCharWidth(int width)2665*8af74909SZhong Yang void ListBoxX::SetAverageCharWidth(int width) {
2666*8af74909SZhong Yang 	aveCharWidth = width;
2667*8af74909SZhong Yang }
2668*8af74909SZhong Yang 
SetVisibleRows(int rows)2669*8af74909SZhong Yang void ListBoxX::SetVisibleRows(int rows) {
2670*8af74909SZhong Yang 	desiredVisibleRows = rows;
2671*8af74909SZhong Yang }
2672*8af74909SZhong Yang 
GetVisibleRows() const2673*8af74909SZhong Yang int ListBoxX::GetVisibleRows() const {
2674*8af74909SZhong Yang 	return desiredVisibleRows;
2675*8af74909SZhong Yang }
2676*8af74909SZhong Yang 
GetHWND() const2677*8af74909SZhong Yang HWND ListBoxX::GetHWND() const noexcept {
2678*8af74909SZhong Yang 	return HwndFromWindowID(GetID());
2679*8af74909SZhong Yang }
2680*8af74909SZhong Yang 
GetDesiredRect()2681*8af74909SZhong Yang PRectangle ListBoxX::GetDesiredRect() {
2682*8af74909SZhong Yang 	PRectangle rcDesired = GetPosition();
2683*8af74909SZhong Yang 
2684*8af74909SZhong Yang 	int rows = Length();
2685*8af74909SZhong Yang 	if ((rows == 0) || (rows > desiredVisibleRows))
2686*8af74909SZhong Yang 		rows = desiredVisibleRows;
2687*8af74909SZhong Yang 	rcDesired.bottom = rcDesired.top + ItemHeight() * rows;
2688*8af74909SZhong Yang 
2689*8af74909SZhong Yang 	int width = MinClientWidth();
2690*8af74909SZhong Yang 	HDC hdc = ::GetDC(lb);
2691*8af74909SZhong Yang 	HFONT oldFont = SelectFont(hdc, fontCopy);
2692*8af74909SZhong Yang 	SIZE textSize = {0, 0};
2693*8af74909SZhong Yang 	int len = 0;
2694*8af74909SZhong Yang 	if (widestItem) {
2695*8af74909SZhong Yang 		len = static_cast<int>(strlen(widestItem));
2696*8af74909SZhong Yang 		if (unicodeMode) {
2697*8af74909SZhong Yang 			const TextWide tbuf(widestItem, unicodeMode);
2698*8af74909SZhong Yang 			::GetTextExtentPoint32W(hdc, tbuf.buffer, tbuf.tlen, &textSize);
2699*8af74909SZhong Yang 		} else {
2700*8af74909SZhong Yang 			::GetTextExtentPoint32A(hdc, widestItem, len, &textSize);
2701*8af74909SZhong Yang 		}
2702*8af74909SZhong Yang 	}
2703*8af74909SZhong Yang 	TEXTMETRIC tm;
2704*8af74909SZhong Yang 	::GetTextMetrics(hdc, &tm);
2705*8af74909SZhong Yang 	maxCharWidth = tm.tmMaxCharWidth;
2706*8af74909SZhong Yang 	SelectFont(hdc, oldFont);
2707*8af74909SZhong Yang 	::ReleaseDC(lb, hdc);
2708*8af74909SZhong Yang 
2709*8af74909SZhong Yang 	const int widthDesired = std::max(textSize.cx, (len + 1) * tm.tmAveCharWidth);
2710*8af74909SZhong Yang 	if (width < widthDesired)
2711*8af74909SZhong Yang 		width = widthDesired;
2712*8af74909SZhong Yang 
2713*8af74909SZhong Yang 	rcDesired.right = rcDesired.left + TextOffset() + width + (TextInset.x * 2);
2714*8af74909SZhong Yang 	if (Length() > rows)
2715*8af74909SZhong Yang 		rcDesired.right += SystemMetricsForDpi(SM_CXVSCROLL, dpi);
2716*8af74909SZhong Yang 
2717*8af74909SZhong Yang 	AdjustWindowRect(&rcDesired, dpi);
2718*8af74909SZhong Yang 	return rcDesired;
2719*8af74909SZhong Yang }
2720*8af74909SZhong Yang 
TextOffset() const2721*8af74909SZhong Yang int ListBoxX::TextOffset() const {
2722*8af74909SZhong Yang 	const int pixWidth = images.GetWidth();
2723*8af74909SZhong Yang 	return static_cast<int>(pixWidth == 0 ? ItemInset.x : ItemInset.x + pixWidth + (ImageInset.x * 2));
2724*8af74909SZhong Yang }
2725*8af74909SZhong Yang 
CaretFromEdge()2726*8af74909SZhong Yang int ListBoxX::CaretFromEdge() {
2727*8af74909SZhong Yang 	PRectangle rc;
2728*8af74909SZhong Yang 	AdjustWindowRect(&rc, dpi);
2729*8af74909SZhong Yang 	return TextOffset() + static_cast<int>(TextInset.x + (0 - rc.left) - 1);
2730*8af74909SZhong Yang }
2731*8af74909SZhong Yang 
Clear()2732*8af74909SZhong Yang void ListBoxX::Clear() {
2733*8af74909SZhong Yang 	ListBox_ResetContent(lb);
2734*8af74909SZhong Yang 	maxItemCharacters = 0;
2735*8af74909SZhong Yang 	widestItem = nullptr;
2736*8af74909SZhong Yang 	lti.Clear();
2737*8af74909SZhong Yang }
2738*8af74909SZhong Yang 
Append(char *,int)2739*8af74909SZhong Yang void ListBoxX::Append(char *, int) {
2740*8af74909SZhong Yang 	// This method is no longer called in Scintilla
2741*8af74909SZhong Yang 	PLATFORM_ASSERT(false);
2742*8af74909SZhong Yang }
2743*8af74909SZhong Yang 
Length()2744*8af74909SZhong Yang int ListBoxX::Length() {
2745*8af74909SZhong Yang 	return lti.Count();
2746*8af74909SZhong Yang }
2747*8af74909SZhong Yang 
Select(int n)2748*8af74909SZhong Yang void ListBoxX::Select(int n) {
2749*8af74909SZhong Yang 	// We are going to scroll to centre on the new selection and then select it, so disable
2750*8af74909SZhong Yang 	// redraw to avoid flicker caused by a painting new selection twice in unselected and then
2751*8af74909SZhong Yang 	// selected states
2752*8af74909SZhong Yang 	SetRedraw(false);
2753*8af74909SZhong Yang 	CentreItem(n);
2754*8af74909SZhong Yang 	ListBox_SetCurSel(lb, n);
2755*8af74909SZhong Yang 	OnSelChange();
2756*8af74909SZhong Yang 	SetRedraw(true);
2757*8af74909SZhong Yang }
2758*8af74909SZhong Yang 
GetSelection()2759*8af74909SZhong Yang int ListBoxX::GetSelection() {
2760*8af74909SZhong Yang 	return ListBox_GetCurSel(lb);
2761*8af74909SZhong Yang }
2762*8af74909SZhong Yang 
2763*8af74909SZhong Yang // This is not actually called at present
Find(const char *)2764*8af74909SZhong Yang int ListBoxX::Find(const char *) {
2765*8af74909SZhong Yang 	return LB_ERR;
2766*8af74909SZhong Yang }
2767*8af74909SZhong Yang 
GetValue(int n,char * value,int len)2768*8af74909SZhong Yang void ListBoxX::GetValue(int n, char *value, int len) {
2769*8af74909SZhong Yang 	const ListItemData item = lti.Get(n);
2770*8af74909SZhong Yang 	strncpy(value, item.text, len);
2771*8af74909SZhong Yang 	value[len-1] = '\0';
2772*8af74909SZhong Yang }
2773*8af74909SZhong Yang 
RegisterImage(int type,const char * xpm_data)2774*8af74909SZhong Yang void ListBoxX::RegisterImage(int type, const char *xpm_data) {
2775*8af74909SZhong Yang 	XPM xpmImage(xpm_data);
2776*8af74909SZhong Yang 	images.Add(type, new RGBAImage(xpmImage));
2777*8af74909SZhong Yang }
2778*8af74909SZhong Yang 
RegisterRGBAImage(int type,int width,int height,const unsigned char * pixelsImage)2779*8af74909SZhong Yang void ListBoxX::RegisterRGBAImage(int type, int width, int height, const unsigned char *pixelsImage) {
2780*8af74909SZhong Yang 	images.Add(type, new RGBAImage(width, height, 1.0, pixelsImage));
2781*8af74909SZhong Yang }
2782*8af74909SZhong Yang 
ClearRegisteredImages()2783*8af74909SZhong Yang void ListBoxX::ClearRegisteredImages() {
2784*8af74909SZhong Yang 	images.Clear();
2785*8af74909SZhong Yang }
2786*8af74909SZhong Yang 
Draw(DRAWITEMSTRUCT * pDrawItem)2787*8af74909SZhong Yang void ListBoxX::Draw(DRAWITEMSTRUCT *pDrawItem) {
2788*8af74909SZhong Yang 	if ((pDrawItem->itemAction == ODA_SELECT) || (pDrawItem->itemAction == ODA_DRAWENTIRE)) {
2789*8af74909SZhong Yang 		RECT rcBox = pDrawItem->rcItem;
2790*8af74909SZhong Yang 		rcBox.left += TextOffset();
2791*8af74909SZhong Yang 		if (pDrawItem->itemState & ODS_SELECTED) {
2792*8af74909SZhong Yang 			RECT rcImage = pDrawItem->rcItem;
2793*8af74909SZhong Yang 			rcImage.right = rcBox.left;
2794*8af74909SZhong Yang 			// The image is not highlighted
2795*8af74909SZhong Yang 			::FillRect(pDrawItem->hDC, &rcImage, reinterpret_cast<HBRUSH>(COLOR_WINDOW+1));
2796*8af74909SZhong Yang 			::FillRect(pDrawItem->hDC, &rcBox, reinterpret_cast<HBRUSH>(COLOR_HIGHLIGHT+1));
2797*8af74909SZhong Yang 			::SetBkColor(pDrawItem->hDC, ::GetSysColor(COLOR_HIGHLIGHT));
2798*8af74909SZhong Yang 			::SetTextColor(pDrawItem->hDC, ::GetSysColor(COLOR_HIGHLIGHTTEXT));
2799*8af74909SZhong Yang 		} else {
2800*8af74909SZhong Yang 			::FillRect(pDrawItem->hDC, &pDrawItem->rcItem, reinterpret_cast<HBRUSH>(COLOR_WINDOW+1));
2801*8af74909SZhong Yang 			::SetBkColor(pDrawItem->hDC, ::GetSysColor(COLOR_WINDOW));
2802*8af74909SZhong Yang 			::SetTextColor(pDrawItem->hDC, ::GetSysColor(COLOR_WINDOWTEXT));
2803*8af74909SZhong Yang 		}
2804*8af74909SZhong Yang 
2805*8af74909SZhong Yang 		const ListItemData item = lti.Get(pDrawItem->itemID);
2806*8af74909SZhong Yang 		const int pixId = item.pixId;
2807*8af74909SZhong Yang 		const char *text = item.text;
2808*8af74909SZhong Yang 		const int len = static_cast<int>(strlen(text));
2809*8af74909SZhong Yang 
2810*8af74909SZhong Yang 		RECT rcText = rcBox;
2811*8af74909SZhong Yang 		::InsetRect(&rcText, static_cast<int>(TextInset.x), static_cast<int>(TextInset.y));
2812*8af74909SZhong Yang 
2813*8af74909SZhong Yang 		if (unicodeMode) {
2814*8af74909SZhong Yang 			const TextWide tbuf(text, unicodeMode);
2815*8af74909SZhong Yang 			::DrawTextW(pDrawItem->hDC, tbuf.buffer, tbuf.tlen, &rcText, DT_NOPREFIX|DT_END_ELLIPSIS|DT_SINGLELINE|DT_NOCLIP);
2816*8af74909SZhong Yang 		} else {
2817*8af74909SZhong Yang 			::DrawTextA(pDrawItem->hDC, text, len, &rcText, DT_NOPREFIX|DT_END_ELLIPSIS|DT_SINGLELINE|DT_NOCLIP);
2818*8af74909SZhong Yang 		}
2819*8af74909SZhong Yang 
2820*8af74909SZhong Yang 		// Draw the image, if any
2821*8af74909SZhong Yang 		const RGBAImage *pimage = images.Get(pixId);
2822*8af74909SZhong Yang 		if (pimage) {
2823*8af74909SZhong Yang 			std::unique_ptr<Surface> surfaceItem(Surface::Allocate(technology));
2824*8af74909SZhong Yang 			if (technology == SCWIN_TECH_GDI) {
2825*8af74909SZhong Yang 				surfaceItem->Init(pDrawItem->hDC, pDrawItem->hwndItem);
2826*8af74909SZhong Yang 				const long left = pDrawItem->rcItem.left + static_cast<int>(ItemInset.x + ImageInset.x);
2827*8af74909SZhong Yang 				const PRectangle rcImage = PRectangle::FromInts(left, pDrawItem->rcItem.top,
2828*8af74909SZhong Yang 					left + images.GetWidth(), pDrawItem->rcItem.bottom);
2829*8af74909SZhong Yang 				surfaceItem->DrawRGBAImage(rcImage,
2830*8af74909SZhong Yang 					pimage->GetWidth(), pimage->GetHeight(), pimage->Pixels());
2831*8af74909SZhong Yang 				::SetTextAlign(pDrawItem->hDC, TA_TOP);
2832*8af74909SZhong Yang 			} else {
2833*8af74909SZhong Yang #if defined(USE_D2D)
2834*8af74909SZhong Yang 				const D2D1_RENDER_TARGET_PROPERTIES props = D2D1::RenderTargetProperties(
2835*8af74909SZhong Yang 					D2D1_RENDER_TARGET_TYPE_DEFAULT,
2836*8af74909SZhong Yang 					D2D1::PixelFormat(
2837*8af74909SZhong Yang 						DXGI_FORMAT_B8G8R8A8_UNORM,
2838*8af74909SZhong Yang 						D2D1_ALPHA_MODE_IGNORE),
2839*8af74909SZhong Yang 					0,
2840*8af74909SZhong Yang 					0,
2841*8af74909SZhong Yang 					D2D1_RENDER_TARGET_USAGE_NONE,
2842*8af74909SZhong Yang 					D2D1_FEATURE_LEVEL_DEFAULT
2843*8af74909SZhong Yang 					);
2844*8af74909SZhong Yang 				ID2D1DCRenderTarget *pDCRT = nullptr;
2845*8af74909SZhong Yang 				HRESULT hr = pD2DFactory->CreateDCRenderTarget(&props, &pDCRT);
2846*8af74909SZhong Yang 				if (SUCCEEDED(hr) && pDCRT) {
2847*8af74909SZhong Yang 					RECT rcWindow;
2848*8af74909SZhong Yang 					GetClientRect(pDrawItem->hwndItem, &rcWindow);
2849*8af74909SZhong Yang 					hr = pDCRT->BindDC(pDrawItem->hDC, &rcWindow);
2850*8af74909SZhong Yang 					if (SUCCEEDED(hr)) {
2851*8af74909SZhong Yang 						surfaceItem->Init(pDCRT, pDrawItem->hwndItem);
2852*8af74909SZhong Yang 						pDCRT->BeginDraw();
2853*8af74909SZhong Yang 						const long left = pDrawItem->rcItem.left + static_cast<long>(ItemInset.x + ImageInset.x);
2854*8af74909SZhong Yang 						const PRectangle rcImage = PRectangle::FromInts(left, pDrawItem->rcItem.top,
2855*8af74909SZhong Yang 							left + images.GetWidth(), pDrawItem->rcItem.bottom);
2856*8af74909SZhong Yang 						surfaceItem->DrawRGBAImage(rcImage,
2857*8af74909SZhong Yang 							pimage->GetWidth(), pimage->GetHeight(), pimage->Pixels());
2858*8af74909SZhong Yang 						pDCRT->EndDraw();
2859*8af74909SZhong Yang 						ReleaseUnknown(pDCRT);
2860*8af74909SZhong Yang 					}
2861*8af74909SZhong Yang 				}
2862*8af74909SZhong Yang #endif
2863*8af74909SZhong Yang 			}
2864*8af74909SZhong Yang 		}
2865*8af74909SZhong Yang 	}
2866*8af74909SZhong Yang }
2867*8af74909SZhong Yang 
AppendListItem(const char * text,const char * numword)2868*8af74909SZhong Yang void ListBoxX::AppendListItem(const char *text, const char *numword) {
2869*8af74909SZhong Yang 	int pixId = -1;
2870*8af74909SZhong Yang 	if (numword) {
2871*8af74909SZhong Yang 		pixId = 0;
2872*8af74909SZhong Yang 		char ch;
2873*8af74909SZhong Yang 		while ((ch = *++numword) != '\0') {
2874*8af74909SZhong Yang 			pixId = 10 * pixId + (ch - '0');
2875*8af74909SZhong Yang 		}
2876*8af74909SZhong Yang 	}
2877*8af74909SZhong Yang 
2878*8af74909SZhong Yang 	lti.AllocItem(text, pixId);
2879*8af74909SZhong Yang 	const unsigned int len = static_cast<unsigned int>(strlen(text));
2880*8af74909SZhong Yang 	if (maxItemCharacters < len) {
2881*8af74909SZhong Yang 		maxItemCharacters = len;
2882*8af74909SZhong Yang 		widestItem = text;
2883*8af74909SZhong Yang 	}
2884*8af74909SZhong Yang }
2885*8af74909SZhong Yang 
SetDelegate(IListBoxDelegate * lbDelegate)2886*8af74909SZhong Yang void ListBoxX::SetDelegate(IListBoxDelegate *lbDelegate) {
2887*8af74909SZhong Yang 	delegate = lbDelegate;
2888*8af74909SZhong Yang }
2889*8af74909SZhong Yang 
SetList(const char * list,char separator,char typesep)2890*8af74909SZhong Yang void ListBoxX::SetList(const char *list, char separator, char typesep) {
2891*8af74909SZhong Yang 	// Turn off redraw while populating the list - this has a significant effect, even if
2892*8af74909SZhong Yang 	// the listbox is not visible.
2893*8af74909SZhong Yang 	SetRedraw(false);
2894*8af74909SZhong Yang 	Clear();
2895*8af74909SZhong Yang 	const size_t size = strlen(list);
2896*8af74909SZhong Yang 	char *words = lti.SetWords(list);
2897*8af74909SZhong Yang 	char *startword = words;
2898*8af74909SZhong Yang 	char *numword = nullptr;
2899*8af74909SZhong Yang 	for (size_t i=0; i < size; i++) {
2900*8af74909SZhong Yang 		if (words[i] == separator) {
2901*8af74909SZhong Yang 			words[i] = '\0';
2902*8af74909SZhong Yang 			if (numword)
2903*8af74909SZhong Yang 				*numword = '\0';
2904*8af74909SZhong Yang 			AppendListItem(startword, numword);
2905*8af74909SZhong Yang 			startword = words + i + 1;
2906*8af74909SZhong Yang 			numword = nullptr;
2907*8af74909SZhong Yang 		} else if (words[i] == typesep) {
2908*8af74909SZhong Yang 			numword = words + i;
2909*8af74909SZhong Yang 		}
2910*8af74909SZhong Yang 	}
2911*8af74909SZhong Yang 	if (startword) {
2912*8af74909SZhong Yang 		if (numword)
2913*8af74909SZhong Yang 			*numword = '\0';
2914*8af74909SZhong Yang 		AppendListItem(startword, numword);
2915*8af74909SZhong Yang 	}
2916*8af74909SZhong Yang 
2917*8af74909SZhong Yang 	// Finally populate the listbox itself with the correct number of items
2918*8af74909SZhong Yang 	const int count = lti.Count();
2919*8af74909SZhong Yang 	::SendMessage(lb, LB_INITSTORAGE, count, 0);
2920*8af74909SZhong Yang 	for (intptr_t j=0; j<count; j++) {
2921*8af74909SZhong Yang 		ListBox_AddItemData(lb, j+1);
2922*8af74909SZhong Yang 	}
2923*8af74909SZhong Yang 	SetRedraw(true);
2924*8af74909SZhong Yang }
2925*8af74909SZhong Yang 
AdjustWindowRect(PRectangle * rc,UINT dpi)2926*8af74909SZhong Yang void ListBoxX::AdjustWindowRect(PRectangle *rc, UINT dpi) noexcept {
2927*8af74909SZhong Yang 	RECT rcw = RectFromPRectangle(*rc);
2928*8af74909SZhong Yang 	if (fnAdjustWindowRectExForDpi) {
2929*8af74909SZhong Yang 		fnAdjustWindowRectExForDpi(&rcw, WS_THICKFRAME, false, WS_EX_WINDOWEDGE, dpi);
2930*8af74909SZhong Yang 	} else {
2931*8af74909SZhong Yang 		::AdjustWindowRectEx(&rcw, WS_THICKFRAME, false, WS_EX_WINDOWEDGE);
2932*8af74909SZhong Yang 	}
2933*8af74909SZhong Yang 	*rc = PRectangle::FromInts(rcw.left, rcw.top, rcw.right, rcw.bottom);
2934*8af74909SZhong Yang }
2935*8af74909SZhong Yang 
ItemHeight() const2936*8af74909SZhong Yang int ListBoxX::ItemHeight() const {
2937*8af74909SZhong Yang 	int itemHeight = lineHeight + (static_cast<int>(TextInset.y) * 2);
2938*8af74909SZhong Yang 	const int pixHeight = images.GetHeight() + (static_cast<int>(ImageInset.y) * 2);
2939*8af74909SZhong Yang 	if (itemHeight < pixHeight) {
2940*8af74909SZhong Yang 		itemHeight = pixHeight;
2941*8af74909SZhong Yang 	}
2942*8af74909SZhong Yang 	return itemHeight;
2943*8af74909SZhong Yang }
2944*8af74909SZhong Yang 
MinClientWidth() const2945*8af74909SZhong Yang int ListBoxX::MinClientWidth() const noexcept {
2946*8af74909SZhong Yang 	return 12 * (aveCharWidth+aveCharWidth/3);
2947*8af74909SZhong Yang }
2948*8af74909SZhong Yang 
MinTrackSize() const2949*8af74909SZhong Yang POINT ListBoxX::MinTrackSize() const {
2950*8af74909SZhong Yang 	PRectangle rc = PRectangle::FromInts(0, 0, MinClientWidth(), ItemHeight());
2951*8af74909SZhong Yang 	AdjustWindowRect(&rc, dpi);
2952*8af74909SZhong Yang 	POINT ret = {static_cast<LONG>(rc.Width()), static_cast<LONG>(rc.Height())};
2953*8af74909SZhong Yang 	return ret;
2954*8af74909SZhong Yang }
2955*8af74909SZhong Yang 
MaxTrackSize() const2956*8af74909SZhong Yang POINT ListBoxX::MaxTrackSize() const {
2957*8af74909SZhong Yang 	PRectangle rc = PRectangle::FromInts(0, 0,
2958*8af74909SZhong Yang 		std::max(static_cast<unsigned int>(MinClientWidth()),
2959*8af74909SZhong Yang 		maxCharWidth * maxItemCharacters + static_cast<int>(TextInset.x) * 2 +
2960*8af74909SZhong Yang 		 TextOffset() + SystemMetricsForDpi(SM_CXVSCROLL, dpi)),
2961*8af74909SZhong Yang 		ItemHeight() * lti.Count());
2962*8af74909SZhong Yang 	AdjustWindowRect(&rc, dpi);
2963*8af74909SZhong Yang 	POINT ret = {static_cast<LONG>(rc.Width()), static_cast<LONG>(rc.Height())};
2964*8af74909SZhong Yang 	return ret;
2965*8af74909SZhong Yang }
2966*8af74909SZhong Yang 
SetRedraw(bool on)2967*8af74909SZhong Yang void ListBoxX::SetRedraw(bool on) noexcept {
2968*8af74909SZhong Yang 	::SendMessage(lb, WM_SETREDRAW, on, 0);
2969*8af74909SZhong Yang 	if (on)
2970*8af74909SZhong Yang 		::InvalidateRect(lb, nullptr, TRUE);
2971*8af74909SZhong Yang }
2972*8af74909SZhong Yang 
ResizeToCursor()2973*8af74909SZhong Yang void ListBoxX::ResizeToCursor() {
2974*8af74909SZhong Yang 	PRectangle rc = GetPosition();
2975*8af74909SZhong Yang 	POINT ptw;
2976*8af74909SZhong Yang 	::GetCursorPos(&ptw);
2977*8af74909SZhong Yang 	const Point pt = PointFromPOINT(ptw) + dragOffset;
2978*8af74909SZhong Yang 
2979*8af74909SZhong Yang 	switch (resizeHit) {
2980*8af74909SZhong Yang 		case HTLEFT:
2981*8af74909SZhong Yang 			rc.left = pt.x;
2982*8af74909SZhong Yang 			break;
2983*8af74909SZhong Yang 		case HTRIGHT:
2984*8af74909SZhong Yang 			rc.right = pt.x;
2985*8af74909SZhong Yang 			break;
2986*8af74909SZhong Yang 		case HTTOP:
2987*8af74909SZhong Yang 			rc.top = pt.y;
2988*8af74909SZhong Yang 			break;
2989*8af74909SZhong Yang 		case HTTOPLEFT:
2990*8af74909SZhong Yang 			rc.top = pt.y;
2991*8af74909SZhong Yang 			rc.left = pt.x;
2992*8af74909SZhong Yang 			break;
2993*8af74909SZhong Yang 		case HTTOPRIGHT:
2994*8af74909SZhong Yang 			rc.top = pt.y;
2995*8af74909SZhong Yang 			rc.right = pt.x;
2996*8af74909SZhong Yang 			break;
2997*8af74909SZhong Yang 		case HTBOTTOM:
2998*8af74909SZhong Yang 			rc.bottom = pt.y;
2999*8af74909SZhong Yang 			break;
3000*8af74909SZhong Yang 		case HTBOTTOMLEFT:
3001*8af74909SZhong Yang 			rc.bottom = pt.y;
3002*8af74909SZhong Yang 			rc.left = pt.x;
3003*8af74909SZhong Yang 			break;
3004*8af74909SZhong Yang 		case HTBOTTOMRIGHT:
3005*8af74909SZhong Yang 			rc.bottom = pt.y;
3006*8af74909SZhong Yang 			rc.right = pt.x;
3007*8af74909SZhong Yang 			break;
3008*8af74909SZhong Yang 	}
3009*8af74909SZhong Yang 
3010*8af74909SZhong Yang 	const POINT ptMin = MinTrackSize();
3011*8af74909SZhong Yang 	const POINT ptMax = MaxTrackSize();
3012*8af74909SZhong Yang 	// We don't allow the left edge to move at present, but just in case
3013*8af74909SZhong Yang 	rc.left = std::clamp(rc.left, rcPreSize.right - ptMax.x, rcPreSize.right - ptMin.x);
3014*8af74909SZhong Yang 	rc.top = std::clamp(rc.top, rcPreSize.bottom - ptMax.y, rcPreSize.bottom - ptMin.y);
3015*8af74909SZhong Yang 	rc.right = std::clamp(rc.right, rcPreSize.left + ptMin.x, rcPreSize.left + ptMax.x);
3016*8af74909SZhong Yang 	rc.bottom = std::clamp(rc.bottom, rcPreSize.top + ptMin.y, rcPreSize.top + ptMax.y);
3017*8af74909SZhong Yang 
3018*8af74909SZhong Yang 	SetPosition(rc);
3019*8af74909SZhong Yang }
3020*8af74909SZhong Yang 
StartResize(WPARAM hitCode)3021*8af74909SZhong Yang void ListBoxX::StartResize(WPARAM hitCode) {
3022*8af74909SZhong Yang 	rcPreSize = GetPosition();
3023*8af74909SZhong Yang 	POINT cursorPos;
3024*8af74909SZhong Yang 	::GetCursorPos(&cursorPos);
3025*8af74909SZhong Yang 
3026*8af74909SZhong Yang 	switch (hitCode) {
3027*8af74909SZhong Yang 		case HTRIGHT:
3028*8af74909SZhong Yang 		case HTBOTTOM:
3029*8af74909SZhong Yang 		case HTBOTTOMRIGHT:
3030*8af74909SZhong Yang 			dragOffset.x = rcPreSize.right - cursorPos.x;
3031*8af74909SZhong Yang 			dragOffset.y = rcPreSize.bottom - cursorPos.y;
3032*8af74909SZhong Yang 			break;
3033*8af74909SZhong Yang 
3034*8af74909SZhong Yang 		case HTTOPRIGHT:
3035*8af74909SZhong Yang 			dragOffset.x = rcPreSize.right - cursorPos.x;
3036*8af74909SZhong Yang 			dragOffset.y = rcPreSize.top - cursorPos.y;
3037*8af74909SZhong Yang 			break;
3038*8af74909SZhong Yang 
3039*8af74909SZhong Yang 		// Note that the current hit test code prevents the left edge cases ever firing
3040*8af74909SZhong Yang 		// as we don't want the left edge to be movable
3041*8af74909SZhong Yang 		case HTLEFT:
3042*8af74909SZhong Yang 		case HTTOP:
3043*8af74909SZhong Yang 		case HTTOPLEFT:
3044*8af74909SZhong Yang 			dragOffset.x = rcPreSize.left - cursorPos.x;
3045*8af74909SZhong Yang 			dragOffset.y = rcPreSize.top - cursorPos.y;
3046*8af74909SZhong Yang 			break;
3047*8af74909SZhong Yang 		case HTBOTTOMLEFT:
3048*8af74909SZhong Yang 			dragOffset.x = rcPreSize.left - cursorPos.x;
3049*8af74909SZhong Yang 			dragOffset.y = rcPreSize.bottom - cursorPos.y;
3050*8af74909SZhong Yang 			break;
3051*8af74909SZhong Yang 
3052*8af74909SZhong Yang 		default:
3053*8af74909SZhong Yang 			return;
3054*8af74909SZhong Yang 	}
3055*8af74909SZhong Yang 
3056*8af74909SZhong Yang 	::SetCapture(GetHWND());
3057*8af74909SZhong Yang 	resizeHit = hitCode;
3058*8af74909SZhong Yang }
3059*8af74909SZhong Yang 
NcHitTest(WPARAM wParam,LPARAM lParam) const3060*8af74909SZhong Yang LRESULT ListBoxX::NcHitTest(WPARAM wParam, LPARAM lParam) const {
3061*8af74909SZhong Yang 	const PRectangle rc = GetPosition();
3062*8af74909SZhong Yang 
3063*8af74909SZhong Yang 	LRESULT hit = ::DefWindowProc(GetHWND(), WM_NCHITTEST, wParam, lParam);
3064*8af74909SZhong Yang 	// There is an apparent bug in the DefWindowProc hit test code whereby it will
3065*8af74909SZhong Yang 	// return HTTOPXXX if the window in question is shorter than the default
3066*8af74909SZhong Yang 	// window caption height + frame, even if one is hovering over the bottom edge of
3067*8af74909SZhong Yang 	// the frame, so workaround that here
3068*8af74909SZhong Yang 	if (hit >= HTTOP && hit <= HTTOPRIGHT) {
3069*8af74909SZhong Yang 		const int minHeight = SystemMetricsForDpi(SM_CYMINTRACK, dpi);
3070*8af74909SZhong Yang 		const int yPos = GET_Y_LPARAM(lParam);
3071*8af74909SZhong Yang 		if ((rc.Height() < minHeight) && (yPos > ((rc.top + rc.bottom)/2))) {
3072*8af74909SZhong Yang 			hit += HTBOTTOM - HTTOP;
3073*8af74909SZhong Yang 		}
3074*8af74909SZhong Yang 	}
3075*8af74909SZhong Yang 
3076*8af74909SZhong Yang 	// Never permit resizing that moves the left edge. Allow movement of top or bottom edge
3077*8af74909SZhong Yang 	// depending on whether the list is above or below the caret
3078*8af74909SZhong Yang 	switch (hit) {
3079*8af74909SZhong Yang 		case HTLEFT:
3080*8af74909SZhong Yang 		case HTTOPLEFT:
3081*8af74909SZhong Yang 		case HTBOTTOMLEFT:
3082*8af74909SZhong Yang 			hit = HTERROR;
3083*8af74909SZhong Yang 			break;
3084*8af74909SZhong Yang 
3085*8af74909SZhong Yang 		case HTTOP:
3086*8af74909SZhong Yang 		case HTTOPRIGHT: {
3087*8af74909SZhong Yang 				// Valid only if caret below list
3088*8af74909SZhong Yang 				if (location.y < rc.top)
3089*8af74909SZhong Yang 					hit = HTERROR;
3090*8af74909SZhong Yang 			}
3091*8af74909SZhong Yang 			break;
3092*8af74909SZhong Yang 
3093*8af74909SZhong Yang 		case HTBOTTOM:
3094*8af74909SZhong Yang 		case HTBOTTOMRIGHT: {
3095*8af74909SZhong Yang 				// Valid only if caret above list
3096*8af74909SZhong Yang 				if (rc.bottom <= location.y)
3097*8af74909SZhong Yang 					hit = HTERROR;
3098*8af74909SZhong Yang 			}
3099*8af74909SZhong Yang 			break;
3100*8af74909SZhong Yang 	}
3101*8af74909SZhong Yang 
3102*8af74909SZhong Yang 	return hit;
3103*8af74909SZhong Yang }
3104*8af74909SZhong Yang 
OnDoubleClick()3105*8af74909SZhong Yang void ListBoxX::OnDoubleClick() {
3106*8af74909SZhong Yang 	if (delegate) {
3107*8af74909SZhong Yang 		ListBoxEvent event(ListBoxEvent::EventType::doubleClick);
3108*8af74909SZhong Yang 		delegate->ListNotify(&event);
3109*8af74909SZhong Yang 	}
3110*8af74909SZhong Yang }
3111*8af74909SZhong Yang 
OnSelChange()3112*8af74909SZhong Yang void ListBoxX::OnSelChange() {
3113*8af74909SZhong Yang 	if (delegate) {
3114*8af74909SZhong Yang 		ListBoxEvent event(ListBoxEvent::EventType::selectionChange);
3115*8af74909SZhong Yang 		delegate->ListNotify(&event);
3116*8af74909SZhong Yang 	}
3117*8af74909SZhong Yang }
3118*8af74909SZhong Yang 
GetClientExtent() const3119*8af74909SZhong Yang POINT ListBoxX::GetClientExtent() const noexcept {
3120*8af74909SZhong Yang 	RECT rc;
3121*8af74909SZhong Yang 	::GetWindowRect(HwndFromWindowID(wid), &rc);
3122*8af74909SZhong Yang 	POINT ret { rc.right - rc.left, rc.bottom - rc.top };
3123*8af74909SZhong Yang 	return ret;
3124*8af74909SZhong Yang }
3125*8af74909SZhong Yang 
CentreItem(int n)3126*8af74909SZhong Yang void ListBoxX::CentreItem(int n) {
3127*8af74909SZhong Yang 	// If below mid point, scroll up to centre, but with more items below if uneven
3128*8af74909SZhong Yang 	if (n >= 0) {
3129*8af74909SZhong Yang 		const POINT extent = GetClientExtent();
3130*8af74909SZhong Yang 		const int visible = extent.y/ItemHeight();
3131*8af74909SZhong Yang 		if (visible < Length()) {
3132*8af74909SZhong Yang 			const int top = ListBox_GetTopIndex(lb);
3133*8af74909SZhong Yang 			const int half = (visible - 1) / 2;
3134*8af74909SZhong Yang 			if (n > (top + half))
3135*8af74909SZhong Yang 				ListBox_SetTopIndex(lb, n - half);
3136*8af74909SZhong Yang 		}
3137*8af74909SZhong Yang 	}
3138*8af74909SZhong Yang }
3139*8af74909SZhong Yang 
3140*8af74909SZhong Yang // Performs a double-buffered paint operation to avoid flicker
Paint(HDC hDC)3141*8af74909SZhong Yang void ListBoxX::Paint(HDC hDC) noexcept {
3142*8af74909SZhong Yang 	const POINT extent = GetClientExtent();
3143*8af74909SZhong Yang 	HBITMAP hBitmap = ::CreateCompatibleBitmap(hDC, extent.x, extent.y);
3144*8af74909SZhong Yang 	HDC bitmapDC = ::CreateCompatibleDC(hDC);
3145*8af74909SZhong Yang 	HBITMAP hBitmapOld = SelectBitmap(bitmapDC, hBitmap);
3146*8af74909SZhong Yang 	// The list background is mainly erased during painting, but can be a small
3147*8af74909SZhong Yang 	// unpainted area when at the end of a non-integrally sized list with a
3148*8af74909SZhong Yang 	// vertical scroll bar
3149*8af74909SZhong Yang 	const RECT rc = { 0, 0, extent.x, extent.y };
3150*8af74909SZhong Yang 	::FillRect(bitmapDC, &rc, reinterpret_cast<HBRUSH>(COLOR_WINDOW+1));
3151*8af74909SZhong Yang 	// Paint the entire client area and vertical scrollbar
3152*8af74909SZhong Yang 	::SendMessage(lb, WM_PRINT, reinterpret_cast<WPARAM>(bitmapDC), PRF_CLIENT|PRF_NONCLIENT);
3153*8af74909SZhong Yang 	::BitBlt(hDC, 0, 0, extent.x, extent.y, bitmapDC, 0, 0, SRCCOPY);
3154*8af74909SZhong Yang 	// Select a stock brush to prevent warnings from BoundsChecker
3155*8af74909SZhong Yang 	SelectBrush(bitmapDC, GetStockBrush(WHITE_BRUSH));
3156*8af74909SZhong Yang 	SelectBitmap(bitmapDC, hBitmapOld);
3157*8af74909SZhong Yang 	::DeleteDC(bitmapDC);
3158*8af74909SZhong Yang 	::DeleteObject(hBitmap);
3159*8af74909SZhong Yang }
3160*8af74909SZhong Yang 
ControlWndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)3161*8af74909SZhong Yang LRESULT PASCAL ListBoxX::ControlWndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam) {
3162*8af74909SZhong Yang 	try {
3163*8af74909SZhong Yang 		ListBoxX *lbx = static_cast<ListBoxX *>(PointerFromWindow(::GetParent(hWnd)));
3164*8af74909SZhong Yang 		switch (iMessage) {
3165*8af74909SZhong Yang 		case WM_ERASEBKGND:
3166*8af74909SZhong Yang 			return TRUE;
3167*8af74909SZhong Yang 
3168*8af74909SZhong Yang 		case WM_PAINT: {
3169*8af74909SZhong Yang 				PAINTSTRUCT ps;
3170*8af74909SZhong Yang 				HDC hDC = ::BeginPaint(hWnd, &ps);
3171*8af74909SZhong Yang 				if (lbx) {
3172*8af74909SZhong Yang 					lbx->Paint(hDC);
3173*8af74909SZhong Yang 				}
3174*8af74909SZhong Yang 				::EndPaint(hWnd, &ps);
3175*8af74909SZhong Yang 			}
3176*8af74909SZhong Yang 			return 0;
3177*8af74909SZhong Yang 
3178*8af74909SZhong Yang 		case WM_MOUSEACTIVATE:
3179*8af74909SZhong Yang 			// This prevents the view activating when the scrollbar is clicked
3180*8af74909SZhong Yang 			return MA_NOACTIVATE;
3181*8af74909SZhong Yang 
3182*8af74909SZhong Yang 		case WM_LBUTTONDOWN: {
3183*8af74909SZhong Yang 				// We must take control of selection to prevent the ListBox activating
3184*8af74909SZhong Yang 				// the popup
3185*8af74909SZhong Yang 				const LRESULT lResult = ::SendMessage(hWnd, LB_ITEMFROMPOINT, 0, lParam);
3186*8af74909SZhong Yang 				const int item = LOWORD(lResult);
3187*8af74909SZhong Yang 				if (HIWORD(lResult) == 0 && item >= 0) {
3188*8af74909SZhong Yang 					ListBox_SetCurSel(hWnd, item);
3189*8af74909SZhong Yang 					if (lbx) {
3190*8af74909SZhong Yang 						lbx->OnSelChange();
3191*8af74909SZhong Yang 					}
3192*8af74909SZhong Yang 				}
3193*8af74909SZhong Yang 			}
3194*8af74909SZhong Yang 			return 0;
3195*8af74909SZhong Yang 
3196*8af74909SZhong Yang 		case WM_LBUTTONUP:
3197*8af74909SZhong Yang 			return 0;
3198*8af74909SZhong Yang 
3199*8af74909SZhong Yang 		case WM_LBUTTONDBLCLK: {
3200*8af74909SZhong Yang 				if (lbx) {
3201*8af74909SZhong Yang 					lbx->OnDoubleClick();
3202*8af74909SZhong Yang 				}
3203*8af74909SZhong Yang 			}
3204*8af74909SZhong Yang 			return 0;
3205*8af74909SZhong Yang 
3206*8af74909SZhong Yang 		case WM_MBUTTONDOWN:
3207*8af74909SZhong Yang 			// disable the scroll wheel button click action
3208*8af74909SZhong Yang 			return 0;
3209*8af74909SZhong Yang 		}
3210*8af74909SZhong Yang 
3211*8af74909SZhong Yang 		WNDPROC prevWndProc = reinterpret_cast<WNDPROC>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
3212*8af74909SZhong Yang 		if (prevWndProc) {
3213*8af74909SZhong Yang 			return ::CallWindowProc(prevWndProc, hWnd, iMessage, wParam, lParam);
3214*8af74909SZhong Yang 		} else {
3215*8af74909SZhong Yang 			return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
3216*8af74909SZhong Yang 		}
3217*8af74909SZhong Yang 	} catch (...) {
3218*8af74909SZhong Yang 	}
3219*8af74909SZhong Yang 	return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
3220*8af74909SZhong Yang }
3221*8af74909SZhong Yang 
WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)3222*8af74909SZhong Yang LRESULT ListBoxX::WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam) {
3223*8af74909SZhong Yang 	switch (iMessage) {
3224*8af74909SZhong Yang 	case WM_CREATE: {
3225*8af74909SZhong Yang 			HINSTANCE hinstanceParent = GetWindowInstance(HwndFromWindow(*parent));
3226*8af74909SZhong Yang 			// Note that LBS_NOINTEGRALHEIGHT is specified to fix cosmetic issue when resizing the list
3227*8af74909SZhong Yang 			// but has useful side effect of speeding up list population significantly
3228*8af74909SZhong Yang 			lb = ::CreateWindowEx(
3229*8af74909SZhong Yang 				0, TEXT("listbox"), TEXT(""),
3230*8af74909SZhong Yang 				WS_CHILD | WS_VSCROLL | WS_VISIBLE |
3231*8af74909SZhong Yang 				LBS_OWNERDRAWFIXED | LBS_NODATA | LBS_NOINTEGRALHEIGHT,
3232*8af74909SZhong Yang 				0, 0, 150,80, hWnd,
3233*8af74909SZhong Yang 				reinterpret_cast<HMENU>(static_cast<ptrdiff_t>(ctrlID)),
3234*8af74909SZhong Yang 				hinstanceParent,
3235*8af74909SZhong Yang 				0);
3236*8af74909SZhong Yang 			WNDPROC prevWndProc = SubclassWindow(lb, ControlWndProc);
3237*8af74909SZhong Yang 			::SetWindowLongPtr(lb, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(prevWndProc));
3238*8af74909SZhong Yang 		}
3239*8af74909SZhong Yang 		break;
3240*8af74909SZhong Yang 
3241*8af74909SZhong Yang 	case WM_SIZE:
3242*8af74909SZhong Yang 		if (lb) {
3243*8af74909SZhong Yang 			SetRedraw(false);
3244*8af74909SZhong Yang 			::SetWindowPos(lb, 0, 0,0, LOWORD(lParam), HIWORD(lParam), SWP_NOZORDER|SWP_NOACTIVATE|SWP_NOMOVE);
3245*8af74909SZhong Yang 			// Ensure the selection remains visible
3246*8af74909SZhong Yang 			CentreItem(GetSelection());
3247*8af74909SZhong Yang 			SetRedraw(true);
3248*8af74909SZhong Yang 		}
3249*8af74909SZhong Yang 		break;
3250*8af74909SZhong Yang 
3251*8af74909SZhong Yang 	case WM_PAINT: {
3252*8af74909SZhong Yang 			PAINTSTRUCT ps;
3253*8af74909SZhong Yang 			::BeginPaint(hWnd, &ps);
3254*8af74909SZhong Yang 			::EndPaint(hWnd, &ps);
3255*8af74909SZhong Yang 		}
3256*8af74909SZhong Yang 		break;
3257*8af74909SZhong Yang 
3258*8af74909SZhong Yang 	case WM_COMMAND:
3259*8af74909SZhong Yang 		// This is not actually needed now - the registered double click action is used
3260*8af74909SZhong Yang 		// directly to action a choice from the list.
3261*8af74909SZhong Yang 		::SendMessage(HwndFromWindow(*parent), iMessage, wParam, lParam);
3262*8af74909SZhong Yang 		break;
3263*8af74909SZhong Yang 
3264*8af74909SZhong Yang 	case WM_MEASUREITEM: {
3265*8af74909SZhong Yang 			MEASUREITEMSTRUCT *pMeasureItem = reinterpret_cast<MEASUREITEMSTRUCT *>(lParam);
3266*8af74909SZhong Yang 			pMeasureItem->itemHeight = ItemHeight();
3267*8af74909SZhong Yang 		}
3268*8af74909SZhong Yang 		break;
3269*8af74909SZhong Yang 
3270*8af74909SZhong Yang 	case WM_DRAWITEM:
3271*8af74909SZhong Yang 		Draw(reinterpret_cast<DRAWITEMSTRUCT *>(lParam));
3272*8af74909SZhong Yang 		break;
3273*8af74909SZhong Yang 
3274*8af74909SZhong Yang 	case WM_DESTROY:
3275*8af74909SZhong Yang 		lb = 0;
3276*8af74909SZhong Yang 		SetWindowPointer(hWnd, nullptr);
3277*8af74909SZhong Yang 		return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
3278*8af74909SZhong Yang 
3279*8af74909SZhong Yang 	case WM_ERASEBKGND:
3280*8af74909SZhong Yang 		// To reduce flicker we can elide background erasure since this window is
3281*8af74909SZhong Yang 		// completely covered by its child.
3282*8af74909SZhong Yang 		return TRUE;
3283*8af74909SZhong Yang 
3284*8af74909SZhong Yang 	case WM_GETMINMAXINFO: {
3285*8af74909SZhong Yang 			MINMAXINFO *minMax = reinterpret_cast<MINMAXINFO*>(lParam);
3286*8af74909SZhong Yang 			minMax->ptMaxTrackSize = MaxTrackSize();
3287*8af74909SZhong Yang 			minMax->ptMinTrackSize = MinTrackSize();
3288*8af74909SZhong Yang 		}
3289*8af74909SZhong Yang 		break;
3290*8af74909SZhong Yang 
3291*8af74909SZhong Yang 	case WM_MOUSEACTIVATE:
3292*8af74909SZhong Yang 		return MA_NOACTIVATE;
3293*8af74909SZhong Yang 
3294*8af74909SZhong Yang 	case WM_NCHITTEST:
3295*8af74909SZhong Yang 		return NcHitTest(wParam, lParam);
3296*8af74909SZhong Yang 
3297*8af74909SZhong Yang 	case WM_NCLBUTTONDOWN:
3298*8af74909SZhong Yang 		// We have to implement our own window resizing because the DefWindowProc
3299*8af74909SZhong Yang 		// implementation insists on activating the resized window
3300*8af74909SZhong Yang 		StartResize(wParam);
3301*8af74909SZhong Yang 		return 0;
3302*8af74909SZhong Yang 
3303*8af74909SZhong Yang 	case WM_MOUSEMOVE: {
3304*8af74909SZhong Yang 			if (resizeHit == 0) {
3305*8af74909SZhong Yang 				return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
3306*8af74909SZhong Yang 			} else {
3307*8af74909SZhong Yang 				ResizeToCursor();
3308*8af74909SZhong Yang 			}
3309*8af74909SZhong Yang 		}
3310*8af74909SZhong Yang 		break;
3311*8af74909SZhong Yang 
3312*8af74909SZhong Yang 	case WM_LBUTTONUP:
3313*8af74909SZhong Yang 	case WM_CANCELMODE:
3314*8af74909SZhong Yang 		if (resizeHit != 0) {
3315*8af74909SZhong Yang 			resizeHit = 0;
3316*8af74909SZhong Yang 			::ReleaseCapture();
3317*8af74909SZhong Yang 		}
3318*8af74909SZhong Yang 		return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
3319*8af74909SZhong Yang 	case WM_MOUSEWHEEL:
3320*8af74909SZhong Yang 		wheelDelta -= GET_WHEEL_DELTA_WPARAM(wParam);
3321*8af74909SZhong Yang 		if (std::abs(wheelDelta) >= WHEEL_DELTA) {
3322*8af74909SZhong Yang 			const int nRows = GetVisibleRows();
3323*8af74909SZhong Yang 			int linesToScroll = 1;
3324*8af74909SZhong Yang 			if (nRows > 1) {
3325*8af74909SZhong Yang 				linesToScroll = nRows - 1;
3326*8af74909SZhong Yang 			}
3327*8af74909SZhong Yang 			if (linesToScroll > 3) {
3328*8af74909SZhong Yang 				linesToScroll = 3;
3329*8af74909SZhong Yang 			}
3330*8af74909SZhong Yang 			linesToScroll *= (wheelDelta / WHEEL_DELTA);
3331*8af74909SZhong Yang 			int top = ListBox_GetTopIndex(lb) + linesToScroll;
3332*8af74909SZhong Yang 			if (top < 0) {
3333*8af74909SZhong Yang 				top = 0;
3334*8af74909SZhong Yang 			}
3335*8af74909SZhong Yang 			ListBox_SetTopIndex(lb, top);
3336*8af74909SZhong Yang 			// update wheel delta residue
3337*8af74909SZhong Yang 			if (wheelDelta >= 0)
3338*8af74909SZhong Yang 				wheelDelta = wheelDelta % WHEEL_DELTA;
3339*8af74909SZhong Yang 			else
3340*8af74909SZhong Yang 				wheelDelta = - (-wheelDelta % WHEEL_DELTA);
3341*8af74909SZhong Yang 		}
3342*8af74909SZhong Yang 		break;
3343*8af74909SZhong Yang 
3344*8af74909SZhong Yang 	default:
3345*8af74909SZhong Yang 		return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
3346*8af74909SZhong Yang 	}
3347*8af74909SZhong Yang 
3348*8af74909SZhong Yang 	return 0;
3349*8af74909SZhong Yang }
3350*8af74909SZhong Yang 
StaticWndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)3351*8af74909SZhong Yang LRESULT PASCAL ListBoxX::StaticWndProc(
3352*8af74909SZhong Yang     HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam) {
3353*8af74909SZhong Yang 	if (iMessage == WM_CREATE) {
3354*8af74909SZhong Yang 		CREATESTRUCT *pCreate = reinterpret_cast<CREATESTRUCT *>(lParam);
3355*8af74909SZhong Yang 		SetWindowPointer(hWnd, pCreate->lpCreateParams);
3356*8af74909SZhong Yang 	}
3357*8af74909SZhong Yang 	// Find C++ object associated with window.
3358*8af74909SZhong Yang 	ListBoxX *lbx = static_cast<ListBoxX *>(PointerFromWindow(hWnd));
3359*8af74909SZhong Yang 	if (lbx) {
3360*8af74909SZhong Yang 		return lbx->WndProc(hWnd, iMessage, wParam, lParam);
3361*8af74909SZhong Yang 	} else {
3362*8af74909SZhong Yang 		return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
3363*8af74909SZhong Yang 	}
3364*8af74909SZhong Yang }
3365*8af74909SZhong Yang 
3366*8af74909SZhong Yang namespace {
3367*8af74909SZhong Yang 
ListBoxX_Register()3368*8af74909SZhong Yang bool ListBoxX_Register() noexcept {
3369*8af74909SZhong Yang 	WNDCLASSEX wndclassc {};
3370*8af74909SZhong Yang 	wndclassc.cbSize = sizeof(wndclassc);
3371*8af74909SZhong Yang 	// We need CS_HREDRAW and CS_VREDRAW because of the ellipsis that might be drawn for
3372*8af74909SZhong Yang 	// truncated items in the list and the appearance/disappearance of the vertical scroll bar.
3373*8af74909SZhong Yang 	// The list repaint is double-buffered to avoid the flicker this would otherwise cause.
3374*8af74909SZhong Yang 	wndclassc.style = CS_GLOBALCLASS | CS_HREDRAW | CS_VREDRAW;
3375*8af74909SZhong Yang 	wndclassc.cbWndExtra = sizeof(ListBoxX *);
3376*8af74909SZhong Yang 	wndclassc.hInstance = hinstPlatformRes;
3377*8af74909SZhong Yang 	wndclassc.lpfnWndProc = ListBoxX::StaticWndProc;
3378*8af74909SZhong Yang 	wndclassc.hCursor = ::LoadCursor(NULL, IDC_ARROW);
3379*8af74909SZhong Yang 	wndclassc.lpszClassName = ListBoxX_ClassName;
3380*8af74909SZhong Yang 
3381*8af74909SZhong Yang 	return ::RegisterClassEx(&wndclassc) != 0;
3382*8af74909SZhong Yang }
3383*8af74909SZhong Yang 
ListBoxX_Unregister()3384*8af74909SZhong Yang void ListBoxX_Unregister() noexcept {
3385*8af74909SZhong Yang 	if (hinstPlatformRes) {
3386*8af74909SZhong Yang 		::UnregisterClass(ListBoxX_ClassName, hinstPlatformRes);
3387*8af74909SZhong Yang 	}
3388*8af74909SZhong Yang }
3389*8af74909SZhong Yang 
3390*8af74909SZhong Yang }
3391*8af74909SZhong Yang 
Menu()3392*8af74909SZhong Yang Menu::Menu() noexcept : mid{} {
3393*8af74909SZhong Yang }
3394*8af74909SZhong Yang 
CreatePopUp()3395*8af74909SZhong Yang void Menu::CreatePopUp() {
3396*8af74909SZhong Yang 	Destroy();
3397*8af74909SZhong Yang 	mid = ::CreatePopupMenu();
3398*8af74909SZhong Yang }
3399*8af74909SZhong Yang 
Destroy()3400*8af74909SZhong Yang void Menu::Destroy() {
3401*8af74909SZhong Yang 	if (mid)
3402*8af74909SZhong Yang 		::DestroyMenu(static_cast<HMENU>(mid));
3403*8af74909SZhong Yang 	mid = 0;
3404*8af74909SZhong Yang }
3405*8af74909SZhong Yang 
Show(Point pt,Window & w)3406*8af74909SZhong Yang void Menu::Show(Point pt, Window &w) {
3407*8af74909SZhong Yang 	::TrackPopupMenu(static_cast<HMENU>(mid),
3408*8af74909SZhong Yang 		TPM_RIGHTBUTTON, static_cast<int>(pt.x - 4), static_cast<int>(pt.y), 0,
3409*8af74909SZhong Yang 		HwndFromWindow(w), nullptr);
3410*8af74909SZhong Yang 	Destroy();
3411*8af74909SZhong Yang }
3412*8af74909SZhong Yang 
3413*8af74909SZhong Yang class DynamicLibraryImpl : public DynamicLibrary {
3414*8af74909SZhong Yang protected:
3415*8af74909SZhong Yang 	HMODULE h;
3416*8af74909SZhong Yang public:
DynamicLibraryImpl(const char * modulePath)3417*8af74909SZhong Yang 	explicit DynamicLibraryImpl(const char *modulePath) noexcept {
3418*8af74909SZhong Yang 		h = ::LoadLibraryA(modulePath);
3419*8af74909SZhong Yang 	}
3420*8af74909SZhong Yang 
~DynamicLibraryImpl()3421*8af74909SZhong Yang 	~DynamicLibraryImpl() override {
3422*8af74909SZhong Yang 		if (h)
3423*8af74909SZhong Yang 			::FreeLibrary(h);
3424*8af74909SZhong Yang 	}
3425*8af74909SZhong Yang 
3426*8af74909SZhong Yang 	// Use GetProcAddress to get a pointer to the relevant function.
FindFunction(const char * name)3427*8af74909SZhong Yang 	Function FindFunction(const char *name) noexcept override {
3428*8af74909SZhong Yang 		if (h) {
3429*8af74909SZhong Yang 			// Use memcpy as it doesn't invoke undefined or conditionally defined behaviour.
3430*8af74909SZhong Yang 			FARPROC fp = ::GetProcAddress(h, name);
3431*8af74909SZhong Yang 			Function f = nullptr;
3432*8af74909SZhong Yang 			static_assert(sizeof(f) == sizeof(fp));
3433*8af74909SZhong Yang 			memcpy(&f, &fp, sizeof(f));
3434*8af74909SZhong Yang 			return f;
3435*8af74909SZhong Yang 		} else {
3436*8af74909SZhong Yang 			return nullptr;
3437*8af74909SZhong Yang 		}
3438*8af74909SZhong Yang 	}
3439*8af74909SZhong Yang 
IsValid()3440*8af74909SZhong Yang 	bool IsValid() noexcept override {
3441*8af74909SZhong Yang 		return h != NULL;
3442*8af74909SZhong Yang 	}
3443*8af74909SZhong Yang };
3444*8af74909SZhong Yang 
Load(const char * modulePath)3445*8af74909SZhong Yang DynamicLibrary *DynamicLibrary::Load(const char *modulePath) {
3446*8af74909SZhong Yang 	return static_cast<DynamicLibrary *>(new DynamicLibraryImpl(modulePath));
3447*8af74909SZhong Yang }
3448*8af74909SZhong Yang 
Chrome()3449*8af74909SZhong Yang ColourDesired Platform::Chrome() {
3450*8af74909SZhong Yang 	return ColourDesired(::GetSysColor(COLOR_3DFACE));
3451*8af74909SZhong Yang }
3452*8af74909SZhong Yang 
ChromeHighlight()3453*8af74909SZhong Yang ColourDesired Platform::ChromeHighlight() {
3454*8af74909SZhong Yang 	return ColourDesired(::GetSysColor(COLOR_3DHIGHLIGHT));
3455*8af74909SZhong Yang }
3456*8af74909SZhong Yang 
DefaultFont()3457*8af74909SZhong Yang const char *Platform::DefaultFont() {
3458*8af74909SZhong Yang 	return "Verdana";
3459*8af74909SZhong Yang }
3460*8af74909SZhong Yang 
DefaultFontSize()3461*8af74909SZhong Yang int Platform::DefaultFontSize() {
3462*8af74909SZhong Yang 	return 8;
3463*8af74909SZhong Yang }
3464*8af74909SZhong Yang 
DoubleClickTime()3465*8af74909SZhong Yang unsigned int Platform::DoubleClickTime() {
3466*8af74909SZhong Yang 	return ::GetDoubleClickTime();
3467*8af74909SZhong Yang }
3468*8af74909SZhong Yang 
DebugDisplay(const char * s)3469*8af74909SZhong Yang void Platform::DebugDisplay(const char *s) {
3470*8af74909SZhong Yang 	::OutputDebugStringA(s);
3471*8af74909SZhong Yang }
3472*8af74909SZhong Yang 
3473*8af74909SZhong Yang //#define TRACE
3474*8af74909SZhong Yang 
3475*8af74909SZhong Yang #ifdef TRACE
DebugPrintf(const char * format,...)3476*8af74909SZhong Yang void Platform::DebugPrintf(const char *format, ...) {
3477*8af74909SZhong Yang 	char buffer[2000];
3478*8af74909SZhong Yang 	va_list pArguments;
3479*8af74909SZhong Yang 	va_start(pArguments, format);
3480*8af74909SZhong Yang 	vsprintf(buffer,format,pArguments);
3481*8af74909SZhong Yang 	va_end(pArguments);
3482*8af74909SZhong Yang 	Platform::DebugDisplay(buffer);
3483*8af74909SZhong Yang }
3484*8af74909SZhong Yang #else
DebugPrintf(const char *,...)3485*8af74909SZhong Yang void Platform::DebugPrintf(const char *, ...) {
3486*8af74909SZhong Yang }
3487*8af74909SZhong Yang #endif
3488*8af74909SZhong Yang 
3489*8af74909SZhong Yang static bool assertionPopUps = true;
3490*8af74909SZhong Yang 
ShowAssertionPopUps(bool assertionPopUps_)3491*8af74909SZhong Yang bool Platform::ShowAssertionPopUps(bool assertionPopUps_) {
3492*8af74909SZhong Yang 	const bool ret = assertionPopUps;
3493*8af74909SZhong Yang 	assertionPopUps = assertionPopUps_;
3494*8af74909SZhong Yang 	return ret;
3495*8af74909SZhong Yang }
3496*8af74909SZhong Yang 
Assert(const char * c,const char * file,int line)3497*8af74909SZhong Yang void Platform::Assert(const char *c, const char *file, int line) {
3498*8af74909SZhong Yang 	char buffer[2000] {};
3499*8af74909SZhong Yang 	sprintf(buffer, "Assertion [%s] failed at %s %d%s", c, file, line, assertionPopUps ? "" : "\r\n");
3500*8af74909SZhong Yang 	if (assertionPopUps) {
3501*8af74909SZhong Yang 		const int idButton = ::MessageBoxA(0, buffer, "Assertion failure",
3502*8af74909SZhong Yang 			MB_ABORTRETRYIGNORE|MB_ICONHAND|MB_SETFOREGROUND|MB_TASKMODAL);
3503*8af74909SZhong Yang 		if (idButton == IDRETRY) {
3504*8af74909SZhong Yang 			::DebugBreak();
3505*8af74909SZhong Yang 		} else if (idButton == IDIGNORE) {
3506*8af74909SZhong Yang 			// all OK
3507*8af74909SZhong Yang 		} else {
3508*8af74909SZhong Yang 			abort();
3509*8af74909SZhong Yang 		}
3510*8af74909SZhong Yang 	} else {
3511*8af74909SZhong Yang 		Platform::DebugDisplay(buffer);
3512*8af74909SZhong Yang 		::DebugBreak();
3513*8af74909SZhong Yang 		abort();
3514*8af74909SZhong Yang 	}
3515*8af74909SZhong Yang }
3516*8af74909SZhong Yang 
Platform_Initialise(void * hInstance)3517*8af74909SZhong Yang void Platform_Initialise(void *hInstance) noexcept {
3518*8af74909SZhong Yang 	hinstPlatformRes = static_cast<HINSTANCE>(hInstance);
3519*8af74909SZhong Yang 	LoadDpiForWindow();
3520*8af74909SZhong Yang 	ListBoxX_Register();
3521*8af74909SZhong Yang }
3522*8af74909SZhong Yang 
Platform_Finalise(bool fromDllMain)3523*8af74909SZhong Yang void Platform_Finalise(bool fromDllMain) noexcept {
3524*8af74909SZhong Yang #if defined(USE_D2D)
3525*8af74909SZhong Yang 	if (!fromDllMain) {
3526*8af74909SZhong Yang 		ReleaseUnknown(defaultRenderingParams);
3527*8af74909SZhong Yang 		ReleaseUnknown(customClearTypeRenderingParams);
3528*8af74909SZhong Yang 		ReleaseUnknown(pIDWriteFactory);
3529*8af74909SZhong Yang 		ReleaseUnknown(pD2DFactory);
3530*8af74909SZhong Yang 		if (hDLLDWrite) {
3531*8af74909SZhong Yang 			FreeLibrary(hDLLDWrite);
3532*8af74909SZhong Yang 			hDLLDWrite = {};
3533*8af74909SZhong Yang 		}
3534*8af74909SZhong Yang 		if (hDLLD2D) {
3535*8af74909SZhong Yang 			FreeLibrary(hDLLD2D);
3536*8af74909SZhong Yang 			hDLLD2D = {};
3537*8af74909SZhong Yang 		}
3538*8af74909SZhong Yang 	}
3539*8af74909SZhong Yang #endif
3540*8af74909SZhong Yang 	if (!fromDllMain && hDLLShcore) {
3541*8af74909SZhong Yang 		FreeLibrary(hDLLShcore);
3542*8af74909SZhong Yang 		hDLLShcore = {};
3543*8af74909SZhong Yang 	}
3544*8af74909SZhong Yang 	ListBoxX_Unregister();
3545*8af74909SZhong Yang }
3546*8af74909SZhong Yang 
3547*8af74909SZhong Yang }
3548